@uniformdev/transformer 1.1.44 → 1.1.46

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";
@@ -6173,7 +6173,7 @@ function createFlattenBlockFieldCommand() {
6173
6173
  const command = new Command15("flatten-block-field");
6174
6174
  command.description(
6175
6175
  "Dissolves a $block parameter by lifting the block content type fields onto the component and extracting block item values into composition instances directly."
6176
- ).option("--componentId <id>", "The ID of the component that owns the block parameter").option("--parameterId <id>", "The ID of the $block parameter to flatten").option("--excludeFields <ids>", "Comma-separated list of block content type field IDs to skip when flattening").hook("preAction", (thisCommand) => {
6176
+ ).option("--componentId <id>", "The ID (or ,;|-separated list of IDs) of the component(s) that own the block parameter").option("--parameterId <id>", "The ID of the $block parameter to flatten").option("--excludeFields <ids>", "Comma-separated list of block content type field IDs to skip when flattening").hook("preAction", (thisCommand) => {
6177
6177
  const opts = thisCommand.opts();
6178
6178
  const requiredOptions = [
6179
6179
  { name: "componentId", flag: "--componentId" },
@@ -6193,31 +6193,49 @@ function createFlattenBlockFieldCommand() {
6193
6193
  excludeFields: opts.excludeFields
6194
6194
  };
6195
6195
  const logger = new Logger();
6196
+ logger.info(`componentId: ${options.componentId}`);
6197
+ logger.info(`parameterId: ${options.parameterId}`);
6198
+ logger.info(`excludeFields: ${options.excludeFields ?? ""}`);
6196
6199
  const fileSystem = new FileSystemService();
6197
6200
  const componentService = new ComponentService(fileSystem);
6198
6201
  const flattener = new BlockFieldFlattenerService(fileSystem, componentService, logger);
6202
+ const componentIds = splitList(options.componentId);
6203
+ const aggregate = {
6204
+ compositionsModified: 0,
6205
+ compositionPatternsModified: 0,
6206
+ componentPatternsModified: 0
6207
+ };
6199
6208
  try {
6200
- const result = await flattener.flatten({
6201
- rootDir: options.rootDir,
6202
- componentsDir: options.componentsDir,
6203
- compositionsDir: options.compositionsDir,
6204
- compositionPatternsDir: options.compositionPatternsDir,
6205
- componentPatternsDir: options.componentPatternsDir,
6206
- contentTypesDir: options.contentTypesDir,
6207
- componentId: options.componentId,
6208
- parameterId: options.parameterId,
6209
- whatIf: options.whatIf ?? false,
6210
- strict: options.strict ?? false,
6211
- excludeFields: options.excludeFields ? options.excludeFields.split(",").map((s) => s.trim()) : []
6212
- });
6209
+ for (const componentId of componentIds) {
6210
+ try {
6211
+ const result = await flattener.flatten({
6212
+ rootDir: options.rootDir,
6213
+ componentsDir: options.componentsDir,
6214
+ compositionsDir: options.compositionsDir,
6215
+ compositionPatternsDir: options.compositionPatternsDir,
6216
+ componentPatternsDir: options.componentPatternsDir,
6217
+ contentTypesDir: options.contentTypesDir,
6218
+ componentId,
6219
+ parameterId: options.parameterId,
6220
+ whatIf: options.whatIf ?? false,
6221
+ strict: options.strict ?? false,
6222
+ excludeFields: options.excludeFields ? options.excludeFields.split(",").map((s) => s.trim()) : []
6223
+ });
6224
+ aggregate.compositionsModified += result.compositionsModified;
6225
+ aggregate.compositionPatternsModified += result.compositionPatternsModified;
6226
+ aggregate.componentPatternsModified += result.componentPatternsModified;
6227
+ } catch (error) {
6228
+ if (error instanceof ComponentNotFoundError || error instanceof PropertyNotFoundError) {
6229
+ logger.warn(error.message);
6230
+ continue;
6231
+ }
6232
+ throw error;
6233
+ }
6234
+ }
6213
6235
  logger.success(
6214
- `Flattened parameter "${options.parameterId}" on component "${options.componentId}": ${result.compositionsModified} composition(s), ${result.compositionPatternsModified} composition pattern(s), ${result.componentPatternsModified} component pattern(s) updated`
6236
+ `Flattened parameter "${options.parameterId}" on component "${options.componentId}": ${aggregate.compositionsModified} composition(s), ${aggregate.compositionPatternsModified} composition pattern(s), ${aggregate.componentPatternsModified} component pattern(s) updated`
6215
6237
  );
6216
6238
  } catch (error) {
6217
- if (error instanceof ComponentNotFoundError || error instanceof PropertyNotFoundError) {
6218
- logger.warn(error.message);
6219
- return;
6220
- }
6221
6239
  if (error instanceof TransformError) {
6222
6240
  logger.error(error.message);
6223
6241
  process.exit(1);
@@ -7141,10 +7159,295 @@ function createSplitContentTypeCommand() {
7141
7159
  return command;
7142
7160
  }
7143
7161
 
7162
+ // src/cli/commands/propagate-root-slot.ts
7163
+ import { Command as Command21 } from "commander";
7164
+
7165
+ // src/core/services/root-slot-propagator.service.ts
7166
+ var RootSlotPropagatorService = class {
7167
+ constructor(fileSystem, componentService, compositionService, logger) {
7168
+ this.fileSystem = fileSystem;
7169
+ this.componentService = componentService;
7170
+ this.compositionService = compositionService;
7171
+ this.logger = logger;
7172
+ }
7173
+ async propagate(options) {
7174
+ const {
7175
+ rootDir,
7176
+ componentsDir,
7177
+ compositionsDir,
7178
+ compositionType,
7179
+ slot,
7180
+ targetComponentType,
7181
+ whatIf,
7182
+ strict
7183
+ } = options;
7184
+ const findOptions = { strict };
7185
+ const compositionTypes = this.parseSeparatedValues(compositionType, strict);
7186
+ const slotNames = splitList(slot);
7187
+ const fullComponentsDir = this.fileSystem.resolvePath(rootDir, componentsDir);
7188
+ const fullCompositionsDir = this.fileSystem.resolvePath(rootDir, compositionsDir);
7189
+ const sourceComponents = [];
7190
+ for (const sourceType of compositionTypes) {
7191
+ this.logger.info(`Loading component: ${sourceType}`);
7192
+ try {
7193
+ const { component: sourceComponent, filePath: sourceFilePath } = await this.componentService.loadComponent(fullComponentsDir, sourceType, findOptions);
7194
+ for (const slotName of slotNames) {
7195
+ const slotDef = this.componentService.findSlot(sourceComponent, slotName, findOptions);
7196
+ if (!slotDef) {
7197
+ this.logger.warn(`Slot "${slotName}" not found on component "${sourceType}" \u2014 skipping`);
7198
+ }
7199
+ }
7200
+ sourceComponents.push({ sourceType, sourceFilePath, sourceComponent });
7201
+ this.logger.debug(`Loaded source component "${sourceType}" from ${sourceFilePath}`);
7202
+ } catch (error) {
7203
+ if (error instanceof ComponentNotFoundError) {
7204
+ this.logger.warn(`Component not found: ${sourceType} (searched: ${fullComponentsDir})`);
7205
+ continue;
7206
+ }
7207
+ throw error;
7208
+ }
7209
+ }
7210
+ this.logger.info(`Loading component: ${targetComponentType}`);
7211
+ const { component: targetComponent, filePath: targetFilePath } = await this.componentService.loadComponent(fullComponentsDir, targetComponentType, findOptions);
7212
+ this.logger.debug(`Loaded target component "${targetComponentType}" from ${targetFilePath}`);
7213
+ const compositions = await this.compositionService.findCompositionsByTypes(
7214
+ fullCompositionsDir,
7215
+ compositionTypes,
7216
+ findOptions
7217
+ );
7218
+ this.logger.debug(
7219
+ `Found ${compositions.length} composition(s) matching types [${compositionTypes.join(", ")}]`
7220
+ );
7221
+ const componentTypesInSlot = /* @__PURE__ */ new Set();
7222
+ for (const { composition } of compositions) {
7223
+ const rootSlots = composition.composition.slots ?? {};
7224
+ for (const slotName of slotNames) {
7225
+ const slotContent = rootSlots[slotName] ?? [];
7226
+ for (const instance of slotContent) {
7227
+ if (instance.type) {
7228
+ componentTypesInSlot.add(instance.type);
7229
+ }
7230
+ }
7231
+ }
7232
+ }
7233
+ const collectedTypes = Array.from(componentTypesInSlot).sort();
7234
+ this.logger.info(
7235
+ `Collected component types from slot contents: [${collectedTypes.join(", ")}]`
7236
+ );
7237
+ let modifiedComponent = { ...targetComponent };
7238
+ let componentModified = false;
7239
+ for (const slotName of slotNames) {
7240
+ const existingSlot = this.componentService.findSlot(modifiedComponent, slotName, findOptions);
7241
+ if (!existingSlot) {
7242
+ this.logger.action(whatIf, "CREATE", `Slot "${slotName}" on ${targetComponentType}`);
7243
+ modifiedComponent = this.componentService.addSlot(modifiedComponent, {
7244
+ id: slotName,
7245
+ name: slotName,
7246
+ allowedComponents: collectedTypes
7247
+ });
7248
+ this.logger.debug(
7249
+ `Slot "${slotName}" created on "${targetComponentType}" with allowedComponents: [${collectedTypes.join(", ")}]`
7250
+ );
7251
+ componentModified = true;
7252
+ } else {
7253
+ const existingAllowed = existingSlot.allowedComponents ?? [];
7254
+ const newTypes = collectedTypes.filter(
7255
+ (t) => !existingAllowed.some(
7256
+ (a) => strict ? a === t : a.toLowerCase() === t.toLowerCase()
7257
+ )
7258
+ );
7259
+ if (newTypes.length > 0) {
7260
+ const merged = [...existingAllowed, ...newTypes].sort();
7261
+ this.logger.action(
7262
+ whatIf,
7263
+ "UPDATE",
7264
+ `Slot "${slotName}" allowedComponents on ${targetComponentType}`
7265
+ );
7266
+ this.logger.detail(`\u2192 Adding: ${newTypes.join(", ")}`);
7267
+ modifiedComponent = this.componentService.updateSlotAllowedComponents(
7268
+ modifiedComponent,
7269
+ slotName,
7270
+ merged,
7271
+ findOptions
7272
+ );
7273
+ componentModified = true;
7274
+ } else {
7275
+ this.logger.info(
7276
+ `Slot "${slotName}" on "${targetComponentType}" already has all required allowedComponents`
7277
+ );
7278
+ }
7279
+ }
7280
+ }
7281
+ if (componentModified && !whatIf) {
7282
+ await this.componentService.saveComponent(targetFilePath, modifiedComponent);
7283
+ }
7284
+ let modifiedCompositions = 0;
7285
+ let propagatedInstances = 0;
7286
+ for (const { composition, filePath } of compositions) {
7287
+ const rootSlots = composition.composition.slots ?? {};
7288
+ const instances = this.compositionService.findComponentInstances(
7289
+ composition,
7290
+ targetComponentType,
7291
+ findOptions
7292
+ );
7293
+ if (instances.length === 0) {
7294
+ this.logger.debug(
7295
+ `Skipping "${filePath}": no instances of ${targetComponentType} found`
7296
+ );
7297
+ continue;
7298
+ }
7299
+ const componentsToPropagate = [];
7300
+ for (const slotName of slotNames) {
7301
+ const slotContent = rootSlots[slotName] ?? [];
7302
+ componentsToPropagate.push(...slotContent);
7303
+ }
7304
+ if (componentsToPropagate.length === 0) {
7305
+ this.logger.debug(
7306
+ `Skipping "${filePath}": slot(s) [${slotNames.join(", ")}] are empty`
7307
+ );
7308
+ continue;
7309
+ }
7310
+ let compositionModified = false;
7311
+ const relativePath = filePath.replace(fullCompositionsDir, "").replace(/^[/\\]/, "");
7312
+ const instanceUpdates = [];
7313
+ for (const { instance, instanceId } of instances) {
7314
+ const instanceName = instance._id ?? instanceId;
7315
+ for (const slotName of slotNames) {
7316
+ const slotContent = rootSlots[slotName] ?? [];
7317
+ if (slotContent.length === 0) continue;
7318
+ const clonedComponents = regenerateIds(
7319
+ slotContent,
7320
+ `${instanceName}.${slotName}`
7321
+ );
7322
+ this.addComponentsToInstanceSlot(instance, slotName, clonedComponents);
7323
+ }
7324
+ compositionModified = true;
7325
+ propagatedInstances++;
7326
+ instanceUpdates.push(
7327
+ `${targetComponentType} "${instanceName}": ${componentsToPropagate.length} component(s) \u2192 [${slotNames.join(", ")}]`
7328
+ );
7329
+ this.logger.debug(
7330
+ `Component ${targetComponentType} "${instanceName}": slot(s) [${slotNames.join(", ")}] updated`
7331
+ );
7332
+ }
7333
+ if (compositionModified) {
7334
+ this.logger.action(whatIf, "UPDATE", `composition/${relativePath}`);
7335
+ for (const update of instanceUpdates) {
7336
+ this.logger.detail(`\u2192 ${update}`);
7337
+ }
7338
+ if (!whatIf) {
7339
+ await this.compositionService.saveComposition(filePath, composition);
7340
+ }
7341
+ modifiedCompositions++;
7342
+ }
7343
+ }
7344
+ return {
7345
+ modifiedComponents: componentModified ? 1 : 0,
7346
+ modifiedCompositions,
7347
+ propagatedInstances
7348
+ };
7349
+ }
7350
+ addComponentsToInstanceSlot(instance, slotName, components) {
7351
+ if (!instance.slots) {
7352
+ instance.slots = {};
7353
+ }
7354
+ if (!instance.slots[slotName]) {
7355
+ instance.slots[slotName] = [];
7356
+ }
7357
+ instance.slots[slotName].push(...components);
7358
+ }
7359
+ parseSeparatedValues(value, strict) {
7360
+ const entries = splitList(value);
7361
+ const normalized = [];
7362
+ for (const entry of entries) {
7363
+ const exists = normalized.some(
7364
+ (existing) => strict ? existing === entry : existing.toLowerCase() === entry.toLowerCase()
7365
+ );
7366
+ if (!exists) {
7367
+ normalized.push(entry);
7368
+ }
7369
+ }
7370
+ return normalized;
7371
+ }
7372
+ };
7373
+
7374
+ // src/cli/commands/propagate-root-slot.ts
7375
+ function createPropagateRootSlotCommand() {
7376
+ const command = new Command21("propagate-root-slot");
7377
+ command.description(
7378
+ "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."
7379
+ ).option(
7380
+ "--compositionType <type>",
7381
+ "The composition type(s) to process. Supports pipe-separated lists (e.g., RelatedTopicPage|LandingPage)"
7382
+ ).option(
7383
+ "--slot <slot>",
7384
+ "The slot name to propagate. The same slot name is used on both the source root and the target component. Supports pipe-separated lists."
7385
+ ).option(
7386
+ "--targetComponentType <type>",
7387
+ "The component type that will receive the slot contents"
7388
+ ).option("--verbose", "Enable verbose output with detailed progress information").hook("preAction", (thisCommand) => {
7389
+ const opts = thisCommand.opts();
7390
+ const requiredOptions = [
7391
+ { name: "compositionType", flag: "--compositionType" },
7392
+ { name: "slot", flag: "--slot" },
7393
+ { name: "targetComponentType", flag: "--targetComponentType" }
7394
+ ];
7395
+ const missing = requiredOptions.filter((opt) => !opts[opt.name]).map((opt) => opt.flag);
7396
+ if (missing.length > 0) {
7397
+ console.error(`error: missing required options: ${missing.join(", ")}`);
7398
+ process.exit(1);
7399
+ }
7400
+ }).action(async (opts, cmd) => {
7401
+ const globalOpts = cmd.optsWithGlobals();
7402
+ const options = {
7403
+ ...globalOpts,
7404
+ compositionType: opts.compositionType,
7405
+ slot: opts.slot,
7406
+ targetComponentType: opts.targetComponentType
7407
+ };
7408
+ const logger = new Logger(opts.verbose ?? false);
7409
+ const fileSystem = new FileSystemService();
7410
+ const componentService = new ComponentService(fileSystem);
7411
+ const compositionService = new CompositionService(fileSystem);
7412
+ const propagator = new RootSlotPropagatorService(
7413
+ fileSystem,
7414
+ componentService,
7415
+ compositionService,
7416
+ logger
7417
+ );
7418
+ try {
7419
+ const result = await propagator.propagate({
7420
+ rootDir: options.rootDir,
7421
+ componentsDir: options.componentsDir,
7422
+ compositionsDir: options.compositionsDir,
7423
+ compositionType: options.compositionType,
7424
+ slot: options.slot,
7425
+ targetComponentType: options.targetComponentType,
7426
+ whatIf: options.whatIf ?? false,
7427
+ strict: options.strict ?? false
7428
+ });
7429
+ logger.success(
7430
+ `Modified ${result.modifiedComponents} component(s), ${result.modifiedCompositions} composition(s), propagated to ${result.propagatedInstances} instance(s)`
7431
+ );
7432
+ } catch (error) {
7433
+ if (error instanceof ComponentNotFoundError) {
7434
+ logger.warn(error.message);
7435
+ return;
7436
+ }
7437
+ if (error instanceof TransformError) {
7438
+ logger.error(error.message);
7439
+ process.exit(1);
7440
+ }
7441
+ throw error;
7442
+ }
7443
+ });
7444
+ return command;
7445
+ }
7446
+
7144
7447
  // package.json
7145
7448
  var package_default = {
7146
7449
  name: "@uniformdev/transformer",
7147
- version: "1.1.44",
7450
+ version: "1.1.46",
7148
7451
  description: "CLI tool for transforming Uniform.dev serialization files offline",
7149
7452
  type: "module",
7150
7453
  bin: {
@@ -7213,7 +7516,7 @@ var package_default = {
7213
7516
  };
7214
7517
 
7215
7518
  // src/cli/index.ts
7216
- var program = new Command21();
7519
+ var program = new Command22();
7217
7520
  var appVersion = package_default.version;
7218
7521
  console.error(`uniform-transform v${appVersion}`);
7219
7522
  program.name("uniform-transform").description("CLI tool for transforming Uniform.dev serialization files offline").version(appVersion);
@@ -7249,5 +7552,6 @@ program.addCommand(createRemoveUnusedContentTypesCommand());
7249
7552
  program.addCommand(createRemoveUnusedComponentTypesCommand());
7250
7553
  program.addCommand(createGenerateMissingProjectMapNodesCommand());
7251
7554
  program.addCommand(createSplitContentTypeCommand());
7555
+ program.addCommand(createPropagateRootSlotCommand());
7252
7556
  program.parse();
7253
7557
  //# sourceMappingURL=index.js.map