@uniformdev/transformer 1.1.39 → 1.1.41
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 +537 -23
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +31 -12
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
4
|
+
import { Command as Command20 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/cli/commands/propagate-root-component-property.ts
|
|
7
7
|
import { Command } from "commander";
|
|
@@ -1611,6 +1611,11 @@ function walkAndRegenerate(value, currentPath) {
|
|
|
1611
1611
|
return value;
|
|
1612
1612
|
}
|
|
1613
1613
|
|
|
1614
|
+
// src/core/utils.ts
|
|
1615
|
+
function splitList(value) {
|
|
1616
|
+
return value.split(/[,;|]/).map((s) => s.trim()).filter((s) => s.length > 0);
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1614
1619
|
// src/core/services/property-propagator.service.ts
|
|
1615
1620
|
var PropertyPropagatorService = class {
|
|
1616
1621
|
constructor(fileSystem, componentService, compositionService, logger) {
|
|
@@ -1654,7 +1659,7 @@ var PropertyPropagatorService = class {
|
|
|
1654
1659
|
this.logger.info(`Loading component: ${targetComponentType}`);
|
|
1655
1660
|
const { component: targetComponent, filePath: targetFilePath } = await this.componentService.loadComponent(fullComponentsDir, targetComponentType, findOptions);
|
|
1656
1661
|
this.logger.debug(`Loaded target component "${targetComponentType}" from ${targetFilePath}`);
|
|
1657
|
-
const propertyNames = property
|
|
1662
|
+
const propertyNames = splitList(property);
|
|
1658
1663
|
const resolvedParams = [];
|
|
1659
1664
|
const resolvedNames = [];
|
|
1660
1665
|
for (const { sourceType, sourceComponent } of sourceComponents) {
|
|
@@ -1892,7 +1897,7 @@ var PropertyPropagatorService = class {
|
|
|
1892
1897
|
};
|
|
1893
1898
|
}
|
|
1894
1899
|
parsePipeSeparatedValues(value, strict) {
|
|
1895
|
-
const entries = value
|
|
1900
|
+
const entries = splitList(value);
|
|
1896
1901
|
const normalized = [];
|
|
1897
1902
|
for (const entry of entries) {
|
|
1898
1903
|
const exists = normalized.some(
|
|
@@ -3850,7 +3855,7 @@ var ComponentAdderService = class {
|
|
|
3850
3855
|
return id1.toLowerCase() === id2.toLowerCase();
|
|
3851
3856
|
}
|
|
3852
3857
|
parseParentComponentTypes(parentComponentType) {
|
|
3853
|
-
return parentComponentType
|
|
3858
|
+
return splitList(parentComponentType);
|
|
3854
3859
|
}
|
|
3855
3860
|
matchesAnyParentType(instanceType, parentTypes, strict) {
|
|
3856
3861
|
return parentTypes.some((pt) => this.compareIds(instanceType, pt, strict));
|
|
@@ -4123,8 +4128,7 @@ var ComponentAdderService = class {
|
|
|
4123
4128
|
}
|
|
4124
4129
|
const newInstance = {
|
|
4125
4130
|
type: componentTypeInPattern,
|
|
4126
|
-
_pattern: pattern.componentPatternId
|
|
4127
|
-
_id: randomUUID()
|
|
4131
|
+
_pattern: pattern.componentPatternId
|
|
4128
4132
|
};
|
|
4129
4133
|
const compositionsResult = await this.addComponentToDirectory(
|
|
4130
4134
|
fullCompositionsDir,
|
|
@@ -4238,13 +4242,19 @@ var ComponentAdderService = class {
|
|
|
4238
4242
|
continue;
|
|
4239
4243
|
}
|
|
4240
4244
|
const rootInstance = composition.composition;
|
|
4245
|
+
const rootPath = `${rootInstance._id ?? ""}${rootInstance.type}`;
|
|
4241
4246
|
let modified = false;
|
|
4242
4247
|
if (this.matchesAnyParentType(rootInstance.type, parentTypes, strict)) {
|
|
4243
4248
|
if (!rootInstance.slots) {
|
|
4244
4249
|
rootInstance.slots = {};
|
|
4245
4250
|
}
|
|
4246
4251
|
const instanceCopy = JSON.parse(JSON.stringify(newInstance));
|
|
4247
|
-
|
|
4252
|
+
if (instanceCopy._pattern !== void 0) {
|
|
4253
|
+
const slotIndex = rootInstance.slots[slot]?.length ?? 0;
|
|
4254
|
+
instanceCopy._id = computeGuidHash(`${rootPath}.${slot}[${slotIndex}]${instanceCopy.type}`);
|
|
4255
|
+
} else {
|
|
4256
|
+
this.regenerateInstanceIds(instanceCopy);
|
|
4257
|
+
}
|
|
4248
4258
|
this.addComponentToSlot(rootInstance.slots, slot, instanceCopy);
|
|
4249
4259
|
modified = true;
|
|
4250
4260
|
}
|
|
@@ -4254,7 +4264,8 @@ var ComponentAdderService = class {
|
|
|
4254
4264
|
parentTypes,
|
|
4255
4265
|
slot,
|
|
4256
4266
|
newInstance,
|
|
4257
|
-
strict
|
|
4267
|
+
strict,
|
|
4268
|
+
rootPath
|
|
4258
4269
|
);
|
|
4259
4270
|
if (nestedModified) {
|
|
4260
4271
|
modified = true;
|
|
@@ -4285,17 +4296,24 @@ var ComponentAdderService = class {
|
|
|
4285
4296
|
}
|
|
4286
4297
|
return filesModified;
|
|
4287
4298
|
}
|
|
4288
|
-
addComponentToNestedSlots(slots, parentTypes, slot, newInstance, strict) {
|
|
4299
|
+
addComponentToNestedSlots(slots, parentTypes, slot, newInstance, strict, pathPrefix) {
|
|
4289
4300
|
let modified = false;
|
|
4290
|
-
for (const slotInstances of Object.
|
|
4301
|
+
for (const [slotName, slotInstances] of Object.entries(slots)) {
|
|
4291
4302
|
if (!Array.isArray(slotInstances)) continue;
|
|
4292
|
-
for (
|
|
4303
|
+
for (let i = 0; i < slotInstances.length; i++) {
|
|
4304
|
+
const instance = slotInstances[i];
|
|
4305
|
+
const instancePath = `${pathPrefix}.${slotName}[${i}]${instance.type}`;
|
|
4293
4306
|
if (this.matchesAnyParentType(instance.type, parentTypes, strict)) {
|
|
4294
4307
|
if (!instance.slots) {
|
|
4295
4308
|
instance.slots = {};
|
|
4296
4309
|
}
|
|
4297
4310
|
const instanceCopy = JSON.parse(JSON.stringify(newInstance));
|
|
4298
|
-
|
|
4311
|
+
if (instanceCopy._pattern !== void 0) {
|
|
4312
|
+
const slotIndex = instance.slots[slot]?.length ?? 0;
|
|
4313
|
+
instanceCopy._id = computeGuidHash(`${instancePath}.${slot}[${slotIndex}]${instanceCopy.type}`);
|
|
4314
|
+
} else {
|
|
4315
|
+
this.regenerateInstanceIds(instanceCopy);
|
|
4316
|
+
}
|
|
4299
4317
|
this.addComponentToSlot(instance.slots, slot, instanceCopy);
|
|
4300
4318
|
modified = true;
|
|
4301
4319
|
}
|
|
@@ -4305,7 +4323,8 @@ var ComponentAdderService = class {
|
|
|
4305
4323
|
parentTypes,
|
|
4306
4324
|
slot,
|
|
4307
4325
|
newInstance,
|
|
4308
|
-
strict
|
|
4326
|
+
strict,
|
|
4327
|
+
instancePath
|
|
4309
4328
|
);
|
|
4310
4329
|
if (nestedModified) {
|
|
4311
4330
|
modified = true;
|
|
@@ -4480,7 +4499,7 @@ var SlotPropagatorService = class {
|
|
|
4480
4499
|
}
|
|
4481
4500
|
this.logger.info(`Loading component: ${targetComponentType}`);
|
|
4482
4501
|
const { component: targetComponent, filePath: targetFilePath } = await this.componentService.loadComponent(fullComponentsDir, targetComponentType, findOptions);
|
|
4483
|
-
const slotNames = slot
|
|
4502
|
+
const slotNames = splitList(slot);
|
|
4484
4503
|
const resolvedSlots = [];
|
|
4485
4504
|
const resolvedSlotIds = [];
|
|
4486
4505
|
for (const { sourceType, sourceComponent } of sourceComponents) {
|
|
@@ -4655,7 +4674,7 @@ var SlotPropagatorService = class {
|
|
|
4655
4674
|
instance.slots[slotName].push(...components);
|
|
4656
4675
|
}
|
|
4657
4676
|
parsePipeSeparatedValues(value, strict) {
|
|
4658
|
-
const entries = value
|
|
4677
|
+
const entries = splitList(value);
|
|
4659
4678
|
const normalized = [];
|
|
4660
4679
|
for (const entry of entries) {
|
|
4661
4680
|
const exists = normalized.some(
|
|
@@ -4802,8 +4821,8 @@ function createConvertCompositionsToEntriesCommand() {
|
|
|
4802
4821
|
logger
|
|
4803
4822
|
);
|
|
4804
4823
|
try {
|
|
4805
|
-
const compositionTypes = options.compositionTypes
|
|
4806
|
-
const parsePipeSeparated = (value) => value ? [...new Set(value
|
|
4824
|
+
const compositionTypes = splitList(options.compositionTypes);
|
|
4825
|
+
const parsePipeSeparated = (value) => value ? [...new Set(splitList(value))] : [];
|
|
4807
4826
|
const componentsToReferences = parsePipeSeparated(options.componentsToReferences);
|
|
4808
4827
|
const componentsToBlocks = parsePipeSeparated(options.componentsToBlocks);
|
|
4809
4828
|
const slotsToReferences = parsePipeSeparated(options.slotsToReferences);
|
|
@@ -5115,7 +5134,7 @@ function createRemoveParameterCommand() {
|
|
|
5115
5134
|
const fileSystem = new FileSystemService();
|
|
5116
5135
|
const componentService = new ComponentService(fileSystem);
|
|
5117
5136
|
const remover = new ParameterRemoverService(fileSystem, componentService, logger);
|
|
5118
|
-
const rawTypes = options.componentType
|
|
5137
|
+
const rawTypes = splitList(options.componentType);
|
|
5119
5138
|
const hasWildcard = rawTypes.includes("*");
|
|
5120
5139
|
let componentTypes;
|
|
5121
5140
|
if (hasWildcard) {
|
|
@@ -5426,7 +5445,7 @@ function createRemoveFieldCommand() {
|
|
|
5426
5445
|
const fileSystem = new FileSystemService();
|
|
5427
5446
|
const componentService = new ComponentService(fileSystem);
|
|
5428
5447
|
const remover = new FieldRemoverService(fileSystem, componentService, logger);
|
|
5429
|
-
const rawTypes = options.componentType
|
|
5448
|
+
const rawTypes = splitList(options.componentType);
|
|
5430
5449
|
const componentTypes = rawTypes.includes("*") ? ["*"] : rawTypes;
|
|
5431
5450
|
const aggregate = {
|
|
5432
5451
|
compositionsModified: 0,
|
|
@@ -6353,7 +6372,7 @@ function createRemoveOrphanEntriesCommand() {
|
|
|
6353
6372
|
};
|
|
6354
6373
|
const logger = new Logger();
|
|
6355
6374
|
logger.info(`rootContentTypes: ${options.rootContentTypes}`);
|
|
6356
|
-
const rootContentTypes = options.rootContentTypes
|
|
6375
|
+
const rootContentTypes = splitList(options.rootContentTypes);
|
|
6357
6376
|
const fileSystem = new FileSystemService();
|
|
6358
6377
|
const service = new OrphanEntryRemoverService(fileSystem, logger);
|
|
6359
6378
|
const result = await service.remove({
|
|
@@ -6380,7 +6399,16 @@ var UnusedContentTypeRemoverService = class {
|
|
|
6380
6399
|
this.logger = logger;
|
|
6381
6400
|
}
|
|
6382
6401
|
async remove(options) {
|
|
6383
|
-
const {
|
|
6402
|
+
const {
|
|
6403
|
+
rootDir,
|
|
6404
|
+
contentTypesDir,
|
|
6405
|
+
entriesDir,
|
|
6406
|
+
compositionsDir,
|
|
6407
|
+
compositionPatternsDir,
|
|
6408
|
+
componentPatternsDir,
|
|
6409
|
+
whatIf,
|
|
6410
|
+
strict
|
|
6411
|
+
} = options;
|
|
6384
6412
|
const contentTypesDirFull = this.fileSystem.resolvePath(rootDir, contentTypesDir);
|
|
6385
6413
|
const entriesDirFull = this.fileSystem.resolvePath(rootDir, entriesDir);
|
|
6386
6414
|
const ctDirExists = await this.fileSystem.fileExists(contentTypesDirFull);
|
|
@@ -6428,6 +6456,27 @@ var UnusedContentTypeRemoverService = class {
|
|
|
6428
6456
|
continue;
|
|
6429
6457
|
}
|
|
6430
6458
|
usedTypeIds.add(entryData.entry.type);
|
|
6459
|
+
const fields = entryData.entry.fields;
|
|
6460
|
+
if (fields && typeof fields === "object") {
|
|
6461
|
+
this.extractBlockTypesFromParameters(fields, usedTypeIds);
|
|
6462
|
+
}
|
|
6463
|
+
}
|
|
6464
|
+
}
|
|
6465
|
+
const dirsToScan = [
|
|
6466
|
+
{ dir: this.fileSystem.resolvePath(rootDir, compositionsDir), label: compositionsDir },
|
|
6467
|
+
{
|
|
6468
|
+
dir: this.fileSystem.resolvePath(rootDir, compositionPatternsDir),
|
|
6469
|
+
label: compositionPatternsDir
|
|
6470
|
+
},
|
|
6471
|
+
{
|
|
6472
|
+
dir: this.fileSystem.resolvePath(rootDir, componentPatternsDir),
|
|
6473
|
+
label: componentPatternsDir
|
|
6474
|
+
}
|
|
6475
|
+
];
|
|
6476
|
+
for (const { dir, label } of dirsToScan) {
|
|
6477
|
+
const blockTypes = await this.collectBlockTypesFromDir(dir, label);
|
|
6478
|
+
for (const t of blockTypes) {
|
|
6479
|
+
usedTypeIds.add(t);
|
|
6431
6480
|
}
|
|
6432
6481
|
}
|
|
6433
6482
|
let removedContentTypes = 0;
|
|
@@ -6454,6 +6503,73 @@ var UnusedContentTypeRemoverService = class {
|
|
|
6454
6503
|
retainedContentTypes
|
|
6455
6504
|
};
|
|
6456
6505
|
}
|
|
6506
|
+
async collectBlockTypesFromDir(dir, label) {
|
|
6507
|
+
const types = /* @__PURE__ */ new Set();
|
|
6508
|
+
const dirExists = await this.fileSystem.fileExists(dir);
|
|
6509
|
+
if (!dirExists) return types;
|
|
6510
|
+
const files = await this.fileSystem.findFiles(dir, "**/*.{json,yaml,yml}");
|
|
6511
|
+
for (const filePath of files) {
|
|
6512
|
+
let data;
|
|
6513
|
+
try {
|
|
6514
|
+
data = await this.fileSystem.readFile(filePath);
|
|
6515
|
+
} catch {
|
|
6516
|
+
this.logger.warn(`Could not read ${label} file: ${filePath} \u2014 skipping`);
|
|
6517
|
+
continue;
|
|
6518
|
+
}
|
|
6519
|
+
this.extractBlockTypesFromFile(data, types);
|
|
6520
|
+
}
|
|
6521
|
+
return types;
|
|
6522
|
+
}
|
|
6523
|
+
extractBlockTypesFromFile(data, types) {
|
|
6524
|
+
if (!data || typeof data !== "object") return;
|
|
6525
|
+
const comp = data.composition;
|
|
6526
|
+
if (!comp || typeof comp !== "object") return;
|
|
6527
|
+
const node = comp;
|
|
6528
|
+
this.extractBlockTypesFromNode(node, types);
|
|
6529
|
+
const overrides = node._overrides;
|
|
6530
|
+
if (overrides && typeof overrides === "object") {
|
|
6531
|
+
for (const override of Object.values(overrides)) {
|
|
6532
|
+
if (!override || typeof override !== "object") continue;
|
|
6533
|
+
const params = override.parameters;
|
|
6534
|
+
if (params && typeof params === "object") {
|
|
6535
|
+
this.extractBlockTypesFromParameters(params, types);
|
|
6536
|
+
}
|
|
6537
|
+
}
|
|
6538
|
+
}
|
|
6539
|
+
}
|
|
6540
|
+
extractBlockTypesFromNode(node, types) {
|
|
6541
|
+
const params = node.parameters;
|
|
6542
|
+
if (params && typeof params === "object") {
|
|
6543
|
+
this.extractBlockTypesFromParameters(params, types);
|
|
6544
|
+
}
|
|
6545
|
+
const slots = node.slots;
|
|
6546
|
+
if (slots && typeof slots === "object") {
|
|
6547
|
+
for (const slotInstances of Object.values(slots)) {
|
|
6548
|
+
if (!Array.isArray(slotInstances)) continue;
|
|
6549
|
+
for (const instance of slotInstances) {
|
|
6550
|
+
if (instance && typeof instance === "object") {
|
|
6551
|
+
this.extractBlockTypesFromNode(instance, types);
|
|
6552
|
+
}
|
|
6553
|
+
}
|
|
6554
|
+
}
|
|
6555
|
+
}
|
|
6556
|
+
}
|
|
6557
|
+
extractBlockTypesFromParameters(params, types) {
|
|
6558
|
+
for (const param of Object.values(params)) {
|
|
6559
|
+
if (!param || typeof param !== "object") continue;
|
|
6560
|
+
const p = param;
|
|
6561
|
+
if (p.type === "$block" && Array.isArray(p.value)) {
|
|
6562
|
+
for (const item of p.value) {
|
|
6563
|
+
if (item && typeof item === "object") {
|
|
6564
|
+
const blockType = item.type;
|
|
6565
|
+
if (typeof blockType === "string") {
|
|
6566
|
+
types.add(blockType);
|
|
6567
|
+
}
|
|
6568
|
+
}
|
|
6569
|
+
}
|
|
6570
|
+
}
|
|
6571
|
+
}
|
|
6572
|
+
}
|
|
6457
6573
|
isUsed(contentTypeId, usedTypeIds, strict) {
|
|
6458
6574
|
if (strict) {
|
|
6459
6575
|
return usedTypeIds.has(contentTypeId);
|
|
@@ -6484,6 +6600,9 @@ function createRemoveUnusedContentTypesCommand() {
|
|
|
6484
6600
|
rootDir: options.rootDir,
|
|
6485
6601
|
contentTypesDir: options.contentTypesDir,
|
|
6486
6602
|
entriesDir: options.entriesDir,
|
|
6603
|
+
compositionsDir: options.compositionsDir,
|
|
6604
|
+
compositionPatternsDir: options.compositionPatternsDir,
|
|
6605
|
+
componentPatternsDir: options.componentPatternsDir,
|
|
6487
6606
|
whatIf: options.whatIf ?? false,
|
|
6488
6607
|
strict: options.strict ?? false
|
|
6489
6608
|
});
|
|
@@ -6494,10 +6613,400 @@ function createRemoveUnusedContentTypesCommand() {
|
|
|
6494
6613
|
return command;
|
|
6495
6614
|
}
|
|
6496
6615
|
|
|
6616
|
+
// src/cli/commands/remove-unused-component-types.ts
|
|
6617
|
+
import { Command as Command18 } from "commander";
|
|
6618
|
+
|
|
6619
|
+
// src/core/services/unused-component-type-remover.service.ts
|
|
6620
|
+
var UnusedComponentTypeRemoverService = class {
|
|
6621
|
+
constructor(fileSystem, logger) {
|
|
6622
|
+
this.fileSystem = fileSystem;
|
|
6623
|
+
this.logger = logger;
|
|
6624
|
+
}
|
|
6625
|
+
async remove(options) {
|
|
6626
|
+
const {
|
|
6627
|
+
rootDir,
|
|
6628
|
+
componentsDir,
|
|
6629
|
+
compositionsDir,
|
|
6630
|
+
compositionPatternsDir,
|
|
6631
|
+
componentPatternsDir,
|
|
6632
|
+
whatIf,
|
|
6633
|
+
strict,
|
|
6634
|
+
excludeComponentTypes = []
|
|
6635
|
+
} = options;
|
|
6636
|
+
const componentsDirFull = this.fileSystem.resolvePath(rootDir, componentsDir);
|
|
6637
|
+
const ctDirExists = await this.fileSystem.fileExists(componentsDirFull);
|
|
6638
|
+
if (!ctDirExists) {
|
|
6639
|
+
this.logger.warn(`Components directory not found: ${componentsDir} \u2014 nothing to do`);
|
|
6640
|
+
return { totalComponentTypes: 0, removedComponentTypes: 0, retainedComponentTypes: 0 };
|
|
6641
|
+
}
|
|
6642
|
+
const componentFiles = await this.fileSystem.findFiles(componentsDirFull, "*.{json,yaml,yml}");
|
|
6643
|
+
if (componentFiles.length === 0) {
|
|
6644
|
+
this.logger.warn("No component files found \u2014 nothing to do");
|
|
6645
|
+
return { totalComponentTypes: 0, removedComponentTypes: 0, retainedComponentTypes: 0 };
|
|
6646
|
+
}
|
|
6647
|
+
this.logger.info(`Found ${componentFiles.length} component file(s)`);
|
|
6648
|
+
const componentMap = /* @__PURE__ */ new Map();
|
|
6649
|
+
for (const filePath of componentFiles) {
|
|
6650
|
+
let comp;
|
|
6651
|
+
try {
|
|
6652
|
+
comp = await this.fileSystem.readFile(filePath);
|
|
6653
|
+
} catch {
|
|
6654
|
+
this.logger.warn(`Could not read component file: ${filePath} \u2014 skipping`);
|
|
6655
|
+
continue;
|
|
6656
|
+
}
|
|
6657
|
+
if (!comp?.id) {
|
|
6658
|
+
this.logger.warn(`Component file missing id: ${filePath} \u2014 skipping`);
|
|
6659
|
+
continue;
|
|
6660
|
+
}
|
|
6661
|
+
componentMap.set(filePath, { filePath, id: comp.id });
|
|
6662
|
+
}
|
|
6663
|
+
const usedTypeIds = /* @__PURE__ */ new Set();
|
|
6664
|
+
await this.collectTypesFromDirectory(
|
|
6665
|
+
this.fileSystem.resolvePath(rootDir, compositionsDir),
|
|
6666
|
+
usedTypeIds,
|
|
6667
|
+
"compositions"
|
|
6668
|
+
);
|
|
6669
|
+
await this.collectTypesFromDirectory(
|
|
6670
|
+
this.fileSystem.resolvePath(rootDir, compositionPatternsDir),
|
|
6671
|
+
usedTypeIds,
|
|
6672
|
+
"composition patterns"
|
|
6673
|
+
);
|
|
6674
|
+
await this.collectTypesFromDirectory(
|
|
6675
|
+
this.fileSystem.resolvePath(rootDir, componentPatternsDir),
|
|
6676
|
+
usedTypeIds,
|
|
6677
|
+
"component patterns"
|
|
6678
|
+
);
|
|
6679
|
+
await this.collectAllowedComponents(componentFiles, usedTypeIds);
|
|
6680
|
+
this.logger.info(`Found ${usedTypeIds.size} referenced component type(s) across all sources`);
|
|
6681
|
+
let removedComponentTypes = 0;
|
|
6682
|
+
let retainedComponentTypes = 0;
|
|
6683
|
+
for (const { filePath, id } of componentMap.values()) {
|
|
6684
|
+
const isUsed = this.isUsed(id, usedTypeIds, strict);
|
|
6685
|
+
const isExcluded = excludeComponentTypes.some(
|
|
6686
|
+
(ex) => strict ? ex === id : ex.toLowerCase() === id.toLowerCase()
|
|
6687
|
+
);
|
|
6688
|
+
if (isUsed || isExcluded) {
|
|
6689
|
+
retainedComponentTypes++;
|
|
6690
|
+
} else {
|
|
6691
|
+
const relPath = this.fileSystem.joinPath(
|
|
6692
|
+
componentsDir,
|
|
6693
|
+
this.fileSystem.getBasename(filePath)
|
|
6694
|
+
);
|
|
6695
|
+
this.logger.action(whatIf, "DELETE", relPath);
|
|
6696
|
+
if (!whatIf) {
|
|
6697
|
+
this.fileSystem.deleteFile(filePath);
|
|
6698
|
+
}
|
|
6699
|
+
removedComponentTypes++;
|
|
6700
|
+
}
|
|
6701
|
+
}
|
|
6702
|
+
return {
|
|
6703
|
+
totalComponentTypes: componentMap.size,
|
|
6704
|
+
removedComponentTypes,
|
|
6705
|
+
retainedComponentTypes
|
|
6706
|
+
};
|
|
6707
|
+
}
|
|
6708
|
+
async collectTypesFromDirectory(dirPath, usedTypeIds, label) {
|
|
6709
|
+
const dirExists = await this.fileSystem.fileExists(dirPath);
|
|
6710
|
+
if (!dirExists) {
|
|
6711
|
+
return;
|
|
6712
|
+
}
|
|
6713
|
+
let files;
|
|
6714
|
+
try {
|
|
6715
|
+
files = await this.fileSystem.findFiles(dirPath, "**/*.{json,yaml,yml}");
|
|
6716
|
+
} catch {
|
|
6717
|
+
return;
|
|
6718
|
+
}
|
|
6719
|
+
if (files.length === 0) {
|
|
6720
|
+
return;
|
|
6721
|
+
}
|
|
6722
|
+
this.logger.info(`Scanning ${files.length} file(s) in ${label}`);
|
|
6723
|
+
for (const filePath of files) {
|
|
6724
|
+
let composition;
|
|
6725
|
+
try {
|
|
6726
|
+
composition = await this.fileSystem.readFile(filePath);
|
|
6727
|
+
} catch {
|
|
6728
|
+
this.logger.warn(`Could not read file: ${filePath} \u2014 skipping`);
|
|
6729
|
+
continue;
|
|
6730
|
+
}
|
|
6731
|
+
if (!composition?.composition) continue;
|
|
6732
|
+
this.collectTypesFromTree(composition.composition, usedTypeIds);
|
|
6733
|
+
}
|
|
6734
|
+
}
|
|
6735
|
+
collectTypesFromTree(node, usedTypeIds) {
|
|
6736
|
+
if (node.type) {
|
|
6737
|
+
usedTypeIds.add(node.type);
|
|
6738
|
+
}
|
|
6739
|
+
if (node.slots) {
|
|
6740
|
+
for (const slotInstances of Object.values(node.slots)) {
|
|
6741
|
+
if (!Array.isArray(slotInstances)) continue;
|
|
6742
|
+
for (const instance of slotInstances) {
|
|
6743
|
+
if (instance && typeof instance === "object") {
|
|
6744
|
+
this.collectTypesFromTree(
|
|
6745
|
+
instance,
|
|
6746
|
+
usedTypeIds
|
|
6747
|
+
);
|
|
6748
|
+
}
|
|
6749
|
+
}
|
|
6750
|
+
}
|
|
6751
|
+
}
|
|
6752
|
+
}
|
|
6753
|
+
async collectAllowedComponents(componentFiles, usedTypeIds) {
|
|
6754
|
+
for (const filePath of componentFiles) {
|
|
6755
|
+
let comp;
|
|
6756
|
+
try {
|
|
6757
|
+
comp = await this.fileSystem.readFile(filePath);
|
|
6758
|
+
} catch {
|
|
6759
|
+
continue;
|
|
6760
|
+
}
|
|
6761
|
+
if (!comp?.slots) continue;
|
|
6762
|
+
for (const slot of comp.slots) {
|
|
6763
|
+
if (!slot.allowedComponents) continue;
|
|
6764
|
+
for (const allowedType of slot.allowedComponents) {
|
|
6765
|
+
usedTypeIds.add(allowedType);
|
|
6766
|
+
}
|
|
6767
|
+
}
|
|
6768
|
+
}
|
|
6769
|
+
}
|
|
6770
|
+
isUsed(componentId, usedTypeIds, strict) {
|
|
6771
|
+
if (strict) {
|
|
6772
|
+
return usedTypeIds.has(componentId);
|
|
6773
|
+
}
|
|
6774
|
+
const lower = componentId.toLowerCase();
|
|
6775
|
+
for (const used of usedTypeIds) {
|
|
6776
|
+
if (used.toLowerCase() === lower) return true;
|
|
6777
|
+
}
|
|
6778
|
+
return false;
|
|
6779
|
+
}
|
|
6780
|
+
};
|
|
6781
|
+
|
|
6782
|
+
// src/cli/commands/remove-unused-component-types.ts
|
|
6783
|
+
function createRemoveUnusedComponentTypesCommand() {
|
|
6784
|
+
const command = new Command18("remove-unused-component-types");
|
|
6785
|
+
command.description(
|
|
6786
|
+
"Removes component definition files that are not referenced in any composition, pattern, or allowedComponents list."
|
|
6787
|
+
).option(
|
|
6788
|
+
"--excludeComponentTypes <types>",
|
|
6789
|
+
'Pipe-separated list of component type IDs to keep even if unused (e.g. "Hero|Button")'
|
|
6790
|
+
).action(async (_opts, cmd) => {
|
|
6791
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
6792
|
+
const options = {
|
|
6793
|
+
...globalOpts
|
|
6794
|
+
};
|
|
6795
|
+
const logger = new Logger();
|
|
6796
|
+
logger.info(`rootDir: ${options.rootDir}`);
|
|
6797
|
+
logger.info(`componentsDir: ${options.componentsDir}`);
|
|
6798
|
+
logger.info(`compositionsDir: ${options.compositionsDir}`);
|
|
6799
|
+
logger.info(`compositionPatternsDir: ${options.compositionPatternsDir}`);
|
|
6800
|
+
logger.info(`componentPatternsDir: ${options.componentPatternsDir}`);
|
|
6801
|
+
logger.info(`excludeComponentTypes: ${options.excludeComponentTypes ?? ""}`);
|
|
6802
|
+
const excludeComponentTypes = options.excludeComponentTypes ? splitList(options.excludeComponentTypes) : [];
|
|
6803
|
+
const fileSystem = new FileSystemService();
|
|
6804
|
+
const service = new UnusedComponentTypeRemoverService(fileSystem, logger);
|
|
6805
|
+
const result = await service.remove({
|
|
6806
|
+
rootDir: options.rootDir,
|
|
6807
|
+
componentsDir: options.componentsDir,
|
|
6808
|
+
compositionsDir: options.compositionsDir,
|
|
6809
|
+
compositionPatternsDir: options.compositionPatternsDir,
|
|
6810
|
+
componentPatternsDir: options.componentPatternsDir,
|
|
6811
|
+
whatIf: options.whatIf ?? false,
|
|
6812
|
+
strict: options.strict ?? false,
|
|
6813
|
+
excludeComponentTypes
|
|
6814
|
+
});
|
|
6815
|
+
logger.success(
|
|
6816
|
+
`Removed ${result.removedComponentTypes} unused component type(s). ${result.retainedComponentTypes} component type(s) retained.`
|
|
6817
|
+
);
|
|
6818
|
+
});
|
|
6819
|
+
return command;
|
|
6820
|
+
}
|
|
6821
|
+
|
|
6822
|
+
// src/cli/commands/generate-missing-project-map-nodes.ts
|
|
6823
|
+
import { Command as Command19 } from "commander";
|
|
6824
|
+
|
|
6825
|
+
// src/core/services/generate-missing-project-map-nodes.service.ts
|
|
6826
|
+
var GenerateMissingProjectMapNodesService = class {
|
|
6827
|
+
constructor(fileSystem, logger) {
|
|
6828
|
+
this.fileSystem = fileSystem;
|
|
6829
|
+
this.logger = logger;
|
|
6830
|
+
}
|
|
6831
|
+
async generate(options) {
|
|
6832
|
+
const { rootDir, projectMapNodesDir, compositionsDir, rootContentTypes, whatIf, strict } = options;
|
|
6833
|
+
const nodesDirFull = this.fileSystem.resolvePath(rootDir, projectMapNodesDir);
|
|
6834
|
+
const nodesDirExists = await this.fileSystem.fileExists(nodesDirFull);
|
|
6835
|
+
if (!nodesDirExists) {
|
|
6836
|
+
this.logger.warn(`Project map nodes directory not found: ${projectMapNodesDir} \u2014 nothing to do`);
|
|
6837
|
+
return { generatedCount: 0, alreadyCoveredCount: 0 };
|
|
6838
|
+
}
|
|
6839
|
+
const nodeFiles = await this.fileSystem.findFiles(nodesDirFull, "**/*.{json,yaml,yml}");
|
|
6840
|
+
if (nodeFiles.length === 0) {
|
|
6841
|
+
this.logger.warn("No project map node files found \u2014 nothing to do");
|
|
6842
|
+
return { generatedCount: 0, alreadyCoveredCount: 0 };
|
|
6843
|
+
}
|
|
6844
|
+
this.logger.info(`Loaded ${nodeFiles.length} project map node(s)`);
|
|
6845
|
+
const allNodes = [];
|
|
6846
|
+
for (const filePath of nodeFiles) {
|
|
6847
|
+
let node;
|
|
6848
|
+
try {
|
|
6849
|
+
node = await this.fileSystem.readFile(filePath);
|
|
6850
|
+
} catch {
|
|
6851
|
+
this.logger.warn(`Could not read project map node file: ${filePath} \u2014 skipping`);
|
|
6852
|
+
continue;
|
|
6853
|
+
}
|
|
6854
|
+
if (!node?.path) {
|
|
6855
|
+
this.logger.warn(`Project map node missing path field: ${filePath} \u2014 skipping`);
|
|
6856
|
+
continue;
|
|
6857
|
+
}
|
|
6858
|
+
allNodes.push({ filePath, node });
|
|
6859
|
+
}
|
|
6860
|
+
const existingPaths = new Set(allNodes.map(({ node }) => node.path));
|
|
6861
|
+
const compositionsDirFull = this.fileSystem.resolvePath(rootDir, compositionsDir);
|
|
6862
|
+
const compositionIdToType = /* @__PURE__ */ new Map();
|
|
6863
|
+
const compositionsDirExists = await this.fileSystem.fileExists(compositionsDirFull);
|
|
6864
|
+
if (!compositionsDirExists) {
|
|
6865
|
+
this.logger.warn(`Compositions directory not found: ${compositionsDir} \u2014 no source nodes will match`);
|
|
6866
|
+
} else {
|
|
6867
|
+
const compositionFiles = await this.fileSystem.findFiles(
|
|
6868
|
+
compositionsDirFull,
|
|
6869
|
+
"**/*.{json,yaml,yml}"
|
|
6870
|
+
);
|
|
6871
|
+
for (const filePath of compositionFiles) {
|
|
6872
|
+
let comp;
|
|
6873
|
+
try {
|
|
6874
|
+
comp = await this.fileSystem.readFile(filePath);
|
|
6875
|
+
} catch {
|
|
6876
|
+
this.logger.warn(`Could not read composition file: ${filePath} \u2014 skipping`);
|
|
6877
|
+
continue;
|
|
6878
|
+
}
|
|
6879
|
+
if (comp?.composition?._id && comp?.composition?.type) {
|
|
6880
|
+
compositionIdToType.set(comp.composition._id, comp.composition.type);
|
|
6881
|
+
}
|
|
6882
|
+
}
|
|
6883
|
+
}
|
|
6884
|
+
const sourceNodes = allNodes.filter(({ node }) => {
|
|
6885
|
+
if (!node.compositionId) return false;
|
|
6886
|
+
const rootType = compositionIdToType.get(node.compositionId);
|
|
6887
|
+
if (!rootType) {
|
|
6888
|
+
this.logger.warn(
|
|
6889
|
+
`No composition found for compositionId "${node.compositionId}" (node path: ${node.path}) \u2014 skipping`
|
|
6890
|
+
);
|
|
6891
|
+
return false;
|
|
6892
|
+
}
|
|
6893
|
+
return this.matchesContentType(rootType, rootContentTypes, strict);
|
|
6894
|
+
});
|
|
6895
|
+
this.logger.info(`${sourceNodes.length} node(s) match root content types`);
|
|
6896
|
+
const ancestorPaths = /* @__PURE__ */ new Set();
|
|
6897
|
+
for (const { node } of sourceNodes) {
|
|
6898
|
+
for (const ancestor of this.computeAncestorPaths(node.path)) {
|
|
6899
|
+
ancestorPaths.add(ancestor);
|
|
6900
|
+
}
|
|
6901
|
+
}
|
|
6902
|
+
const missingPaths = [];
|
|
6903
|
+
let alreadyCoveredCount = 0;
|
|
6904
|
+
for (const ancestorPath of ancestorPaths) {
|
|
6905
|
+
if (existingPaths.has(ancestorPath)) {
|
|
6906
|
+
alreadyCoveredCount++;
|
|
6907
|
+
} else {
|
|
6908
|
+
missingPaths.push(ancestorPath);
|
|
6909
|
+
}
|
|
6910
|
+
}
|
|
6911
|
+
this.logger.info(
|
|
6912
|
+
`Collected ${ancestorPaths.size} unique ancestor path(s), ${alreadyCoveredCount} already covered`
|
|
6913
|
+
);
|
|
6914
|
+
let generatedCount = 0;
|
|
6915
|
+
for (const missingPath of missingPaths) {
|
|
6916
|
+
const fileName = this.pathToFileName(missingPath);
|
|
6917
|
+
const nodeId = this.pathToNodeId(missingPath);
|
|
6918
|
+
const destFilePath = this.fileSystem.resolvePath(nodesDirFull, fileName);
|
|
6919
|
+
const relPath = this.fileSystem.joinPath(projectMapNodesDir, fileName);
|
|
6920
|
+
this.logger.action(whatIf, "CREATE", `${relPath} (path: ${missingPath}, type: group)`);
|
|
6921
|
+
if (!whatIf) {
|
|
6922
|
+
const newNode = {
|
|
6923
|
+
id: nodeId,
|
|
6924
|
+
path: missingPath,
|
|
6925
|
+
type: "group"
|
|
6926
|
+
};
|
|
6927
|
+
await this.fileSystem.writeFile(destFilePath, newNode);
|
|
6928
|
+
}
|
|
6929
|
+
generatedCount++;
|
|
6930
|
+
}
|
|
6931
|
+
return { generatedCount, alreadyCoveredCount };
|
|
6932
|
+
}
|
|
6933
|
+
computeAncestorPaths(nodePath) {
|
|
6934
|
+
const segments = nodePath.split("/").filter((s) => s.length > 0);
|
|
6935
|
+
const ancestors = [];
|
|
6936
|
+
ancestors.push("/");
|
|
6937
|
+
for (let i = 1; i < segments.length; i++) {
|
|
6938
|
+
ancestors.push("/" + segments.slice(0, i).join("/"));
|
|
6939
|
+
}
|
|
6940
|
+
return ancestors;
|
|
6941
|
+
}
|
|
6942
|
+
pathToNodeId(nodePath) {
|
|
6943
|
+
if (nodePath === "/") return "pmn-group-root";
|
|
6944
|
+
const slug = nodePath.split("/").filter((s) => s.length > 0).join("-");
|
|
6945
|
+
return `pmn-group-${slug}`;
|
|
6946
|
+
}
|
|
6947
|
+
pathToFileName(nodePath) {
|
|
6948
|
+
if (nodePath === "/") return "root-node.yaml";
|
|
6949
|
+
const slug = nodePath.split("/").filter((s) => s.length > 0).join("-");
|
|
6950
|
+
return `${slug}-node.yaml`;
|
|
6951
|
+
}
|
|
6952
|
+
matchesContentType(rootType, rootContentTypes, strict) {
|
|
6953
|
+
for (const ct of rootContentTypes) {
|
|
6954
|
+
if (strict) {
|
|
6955
|
+
if (rootType === ct) return true;
|
|
6956
|
+
} else {
|
|
6957
|
+
if (rootType.toLowerCase() === ct.toLowerCase()) return true;
|
|
6958
|
+
}
|
|
6959
|
+
}
|
|
6960
|
+
return false;
|
|
6961
|
+
}
|
|
6962
|
+
};
|
|
6963
|
+
|
|
6964
|
+
// src/cli/commands/generate-missing-project-map-nodes.ts
|
|
6965
|
+
function createGenerateMissingProjectMapNodesCommand() {
|
|
6966
|
+
const command = new Command19("generate-missing-project-map-nodes");
|
|
6967
|
+
command.description(
|
|
6968
|
+
"Creates group project map nodes for ancestor path segments that are missing from the project map. Only paths reachable from compositions whose root type matches --rootContentTypes are considered."
|
|
6969
|
+
).option(
|
|
6970
|
+
"--rootContentTypes <types>",
|
|
6971
|
+
'Pipe-separated list of root component types identifying the leaf-page compositions whose ancestor paths should be filled in (e.g. "BlogPost|NewsArticle")'
|
|
6972
|
+
).hook("preAction", (thisCommand) => {
|
|
6973
|
+
const opts = thisCommand.opts();
|
|
6974
|
+
const requiredOptions = [{ name: "rootContentTypes", flag: "--rootContentTypes" }];
|
|
6975
|
+
const missing = requiredOptions.filter((opt) => !opts[opt.name]).map((opt) => opt.flag);
|
|
6976
|
+
if (missing.length > 0) {
|
|
6977
|
+
console.error(`error: missing required options: ${missing.join(", ")}`);
|
|
6978
|
+
process.exit(1);
|
|
6979
|
+
}
|
|
6980
|
+
}).action(async (opts, cmd) => {
|
|
6981
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
6982
|
+
const options = {
|
|
6983
|
+
...globalOpts,
|
|
6984
|
+
rootContentTypes: opts.rootContentTypes
|
|
6985
|
+
};
|
|
6986
|
+
const logger = new Logger();
|
|
6987
|
+
logger.info(`rootContentTypes: ${options.rootContentTypes}`);
|
|
6988
|
+
const rootContentTypes = splitList(options.rootContentTypes);
|
|
6989
|
+
const fileSystem = new FileSystemService();
|
|
6990
|
+
const service = new GenerateMissingProjectMapNodesService(fileSystem, logger);
|
|
6991
|
+
const result = await service.generate({
|
|
6992
|
+
rootDir: options.rootDir,
|
|
6993
|
+
projectMapNodesDir: options.projectMapNodesDir,
|
|
6994
|
+
compositionsDir: options.compositionsDir,
|
|
6995
|
+
rootContentTypes,
|
|
6996
|
+
whatIf: options.whatIf ?? false,
|
|
6997
|
+
strict: options.strict ?? false
|
|
6998
|
+
});
|
|
6999
|
+
logger.success(
|
|
7000
|
+
`Created ${result.generatedCount} missing project map node(s). ${result.alreadyCoveredCount} ancestor path(s) already existed.`
|
|
7001
|
+
);
|
|
7002
|
+
});
|
|
7003
|
+
return command;
|
|
7004
|
+
}
|
|
7005
|
+
|
|
6497
7006
|
// package.json
|
|
6498
7007
|
var package_default = {
|
|
6499
7008
|
name: "@uniformdev/transformer",
|
|
6500
|
-
version: "1.1.
|
|
7009
|
+
version: "1.1.41",
|
|
6501
7010
|
description: "CLI tool for transforming Uniform.dev serialization files offline",
|
|
6502
7011
|
type: "module",
|
|
6503
7012
|
bin: {
|
|
@@ -6566,7 +7075,7 @@ var package_default = {
|
|
|
6566
7075
|
};
|
|
6567
7076
|
|
|
6568
7077
|
// src/cli/index.ts
|
|
6569
|
-
var program = new
|
|
7078
|
+
var program = new Command20();
|
|
6570
7079
|
var appVersion = package_default.version;
|
|
6571
7080
|
console.error(`uniform-transform v${appVersion}`);
|
|
6572
7081
|
program.name("uniform-transform").description("CLI tool for transforming Uniform.dev serialization files offline").version(appVersion);
|
|
@@ -6579,6 +7088,9 @@ program.requiredOption("--rootDir <path>", "Path to the serialization project ro
|
|
|
6579
7088
|
"Project map definitions directory name",
|
|
6580
7089
|
"projectMapDefinition"
|
|
6581
7090
|
).option("--projectMapNodesDir <dir>", "Project map nodes directory name", "projectMapNode").option("--quirksDir <dir>", "Quirks directory name", "quirk");
|
|
7091
|
+
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
7092
|
+
console.error(`[INFO] ${actionCommand.name()} args: ${JSON.stringify(actionCommand.optsWithGlobals())}`);
|
|
7093
|
+
});
|
|
6582
7094
|
program.addCommand(createPropagateRootComponentPropertyCommand());
|
|
6583
7095
|
program.addCommand(createFindCompositionPatternCandidatesCommand());
|
|
6584
7096
|
program.addCommand(createUnpackSerializationCommand());
|
|
@@ -6596,5 +7108,7 @@ program.addCommand(createAddContentTypeFieldCommand());
|
|
|
6596
7108
|
program.addCommand(createFlattenBlockFieldCommand());
|
|
6597
7109
|
program.addCommand(createRemoveOrphanEntriesCommand());
|
|
6598
7110
|
program.addCommand(createRemoveUnusedContentTypesCommand());
|
|
7111
|
+
program.addCommand(createRemoveUnusedComponentTypesCommand());
|
|
7112
|
+
program.addCommand(createGenerateMissingProjectMapNodesCommand());
|
|
6599
7113
|
program.parse();
|
|
6600
7114
|
//# sourceMappingURL=index.js.map
|