@uniformdev/transformer 1.1.39 → 1.1.40
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 +509 -13
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +23 -9
- 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";
|
|
@@ -4123,8 +4123,7 @@ var ComponentAdderService = class {
|
|
|
4123
4123
|
}
|
|
4124
4124
|
const newInstance = {
|
|
4125
4125
|
type: componentTypeInPattern,
|
|
4126
|
-
_pattern: pattern.componentPatternId
|
|
4127
|
-
_id: randomUUID()
|
|
4126
|
+
_pattern: pattern.componentPatternId
|
|
4128
4127
|
};
|
|
4129
4128
|
const compositionsResult = await this.addComponentToDirectory(
|
|
4130
4129
|
fullCompositionsDir,
|
|
@@ -4238,13 +4237,19 @@ var ComponentAdderService = class {
|
|
|
4238
4237
|
continue;
|
|
4239
4238
|
}
|
|
4240
4239
|
const rootInstance = composition.composition;
|
|
4240
|
+
const rootPath = `${rootInstance._id ?? ""}${rootInstance.type}`;
|
|
4241
4241
|
let modified = false;
|
|
4242
4242
|
if (this.matchesAnyParentType(rootInstance.type, parentTypes, strict)) {
|
|
4243
4243
|
if (!rootInstance.slots) {
|
|
4244
4244
|
rootInstance.slots = {};
|
|
4245
4245
|
}
|
|
4246
4246
|
const instanceCopy = JSON.parse(JSON.stringify(newInstance));
|
|
4247
|
-
|
|
4247
|
+
if (instanceCopy._pattern !== void 0) {
|
|
4248
|
+
const slotIndex = rootInstance.slots[slot]?.length ?? 0;
|
|
4249
|
+
instanceCopy._id = computeGuidHash(`${rootPath}.${slot}[${slotIndex}]${instanceCopy.type}`);
|
|
4250
|
+
} else {
|
|
4251
|
+
this.regenerateInstanceIds(instanceCopy);
|
|
4252
|
+
}
|
|
4248
4253
|
this.addComponentToSlot(rootInstance.slots, slot, instanceCopy);
|
|
4249
4254
|
modified = true;
|
|
4250
4255
|
}
|
|
@@ -4254,7 +4259,8 @@ var ComponentAdderService = class {
|
|
|
4254
4259
|
parentTypes,
|
|
4255
4260
|
slot,
|
|
4256
4261
|
newInstance,
|
|
4257
|
-
strict
|
|
4262
|
+
strict,
|
|
4263
|
+
rootPath
|
|
4258
4264
|
);
|
|
4259
4265
|
if (nestedModified) {
|
|
4260
4266
|
modified = true;
|
|
@@ -4285,17 +4291,24 @@ var ComponentAdderService = class {
|
|
|
4285
4291
|
}
|
|
4286
4292
|
return filesModified;
|
|
4287
4293
|
}
|
|
4288
|
-
addComponentToNestedSlots(slots, parentTypes, slot, newInstance, strict) {
|
|
4294
|
+
addComponentToNestedSlots(slots, parentTypes, slot, newInstance, strict, pathPrefix) {
|
|
4289
4295
|
let modified = false;
|
|
4290
|
-
for (const slotInstances of Object.
|
|
4296
|
+
for (const [slotName, slotInstances] of Object.entries(slots)) {
|
|
4291
4297
|
if (!Array.isArray(slotInstances)) continue;
|
|
4292
|
-
for (
|
|
4298
|
+
for (let i = 0; i < slotInstances.length; i++) {
|
|
4299
|
+
const instance = slotInstances[i];
|
|
4300
|
+
const instancePath = `${pathPrefix}.${slotName}[${i}]${instance.type}`;
|
|
4293
4301
|
if (this.matchesAnyParentType(instance.type, parentTypes, strict)) {
|
|
4294
4302
|
if (!instance.slots) {
|
|
4295
4303
|
instance.slots = {};
|
|
4296
4304
|
}
|
|
4297
4305
|
const instanceCopy = JSON.parse(JSON.stringify(newInstance));
|
|
4298
|
-
|
|
4306
|
+
if (instanceCopy._pattern !== void 0) {
|
|
4307
|
+
const slotIndex = instance.slots[slot]?.length ?? 0;
|
|
4308
|
+
instanceCopy._id = computeGuidHash(`${instancePath}.${slot}[${slotIndex}]${instanceCopy.type}`);
|
|
4309
|
+
} else {
|
|
4310
|
+
this.regenerateInstanceIds(instanceCopy);
|
|
4311
|
+
}
|
|
4299
4312
|
this.addComponentToSlot(instance.slots, slot, instanceCopy);
|
|
4300
4313
|
modified = true;
|
|
4301
4314
|
}
|
|
@@ -4305,7 +4318,8 @@ var ComponentAdderService = class {
|
|
|
4305
4318
|
parentTypes,
|
|
4306
4319
|
slot,
|
|
4307
4320
|
newInstance,
|
|
4308
|
-
strict
|
|
4321
|
+
strict,
|
|
4322
|
+
instancePath
|
|
4309
4323
|
);
|
|
4310
4324
|
if (nestedModified) {
|
|
4311
4325
|
modified = true;
|
|
@@ -6380,7 +6394,16 @@ var UnusedContentTypeRemoverService = class {
|
|
|
6380
6394
|
this.logger = logger;
|
|
6381
6395
|
}
|
|
6382
6396
|
async remove(options) {
|
|
6383
|
-
const {
|
|
6397
|
+
const {
|
|
6398
|
+
rootDir,
|
|
6399
|
+
contentTypesDir,
|
|
6400
|
+
entriesDir,
|
|
6401
|
+
compositionsDir,
|
|
6402
|
+
compositionPatternsDir,
|
|
6403
|
+
componentPatternsDir,
|
|
6404
|
+
whatIf,
|
|
6405
|
+
strict
|
|
6406
|
+
} = options;
|
|
6384
6407
|
const contentTypesDirFull = this.fileSystem.resolvePath(rootDir, contentTypesDir);
|
|
6385
6408
|
const entriesDirFull = this.fileSystem.resolvePath(rootDir, entriesDir);
|
|
6386
6409
|
const ctDirExists = await this.fileSystem.fileExists(contentTypesDirFull);
|
|
@@ -6428,6 +6451,27 @@ var UnusedContentTypeRemoverService = class {
|
|
|
6428
6451
|
continue;
|
|
6429
6452
|
}
|
|
6430
6453
|
usedTypeIds.add(entryData.entry.type);
|
|
6454
|
+
const fields = entryData.entry.fields;
|
|
6455
|
+
if (fields && typeof fields === "object") {
|
|
6456
|
+
this.extractBlockTypesFromParameters(fields, usedTypeIds);
|
|
6457
|
+
}
|
|
6458
|
+
}
|
|
6459
|
+
}
|
|
6460
|
+
const dirsToScan = [
|
|
6461
|
+
{ dir: this.fileSystem.resolvePath(rootDir, compositionsDir), label: compositionsDir },
|
|
6462
|
+
{
|
|
6463
|
+
dir: this.fileSystem.resolvePath(rootDir, compositionPatternsDir),
|
|
6464
|
+
label: compositionPatternsDir
|
|
6465
|
+
},
|
|
6466
|
+
{
|
|
6467
|
+
dir: this.fileSystem.resolvePath(rootDir, componentPatternsDir),
|
|
6468
|
+
label: componentPatternsDir
|
|
6469
|
+
}
|
|
6470
|
+
];
|
|
6471
|
+
for (const { dir, label } of dirsToScan) {
|
|
6472
|
+
const blockTypes = await this.collectBlockTypesFromDir(dir, label);
|
|
6473
|
+
for (const t of blockTypes) {
|
|
6474
|
+
usedTypeIds.add(t);
|
|
6431
6475
|
}
|
|
6432
6476
|
}
|
|
6433
6477
|
let removedContentTypes = 0;
|
|
@@ -6454,6 +6498,73 @@ var UnusedContentTypeRemoverService = class {
|
|
|
6454
6498
|
retainedContentTypes
|
|
6455
6499
|
};
|
|
6456
6500
|
}
|
|
6501
|
+
async collectBlockTypesFromDir(dir, label) {
|
|
6502
|
+
const types = /* @__PURE__ */ new Set();
|
|
6503
|
+
const dirExists = await this.fileSystem.fileExists(dir);
|
|
6504
|
+
if (!dirExists) return types;
|
|
6505
|
+
const files = await this.fileSystem.findFiles(dir, "**/*.{json,yaml,yml}");
|
|
6506
|
+
for (const filePath of files) {
|
|
6507
|
+
let data;
|
|
6508
|
+
try {
|
|
6509
|
+
data = await this.fileSystem.readFile(filePath);
|
|
6510
|
+
} catch {
|
|
6511
|
+
this.logger.warn(`Could not read ${label} file: ${filePath} \u2014 skipping`);
|
|
6512
|
+
continue;
|
|
6513
|
+
}
|
|
6514
|
+
this.extractBlockTypesFromFile(data, types);
|
|
6515
|
+
}
|
|
6516
|
+
return types;
|
|
6517
|
+
}
|
|
6518
|
+
extractBlockTypesFromFile(data, types) {
|
|
6519
|
+
if (!data || typeof data !== "object") return;
|
|
6520
|
+
const comp = data.composition;
|
|
6521
|
+
if (!comp || typeof comp !== "object") return;
|
|
6522
|
+
const node = comp;
|
|
6523
|
+
this.extractBlockTypesFromNode(node, types);
|
|
6524
|
+
const overrides = node._overrides;
|
|
6525
|
+
if (overrides && typeof overrides === "object") {
|
|
6526
|
+
for (const override of Object.values(overrides)) {
|
|
6527
|
+
if (!override || typeof override !== "object") continue;
|
|
6528
|
+
const params = override.parameters;
|
|
6529
|
+
if (params && typeof params === "object") {
|
|
6530
|
+
this.extractBlockTypesFromParameters(params, types);
|
|
6531
|
+
}
|
|
6532
|
+
}
|
|
6533
|
+
}
|
|
6534
|
+
}
|
|
6535
|
+
extractBlockTypesFromNode(node, types) {
|
|
6536
|
+
const params = node.parameters;
|
|
6537
|
+
if (params && typeof params === "object") {
|
|
6538
|
+
this.extractBlockTypesFromParameters(params, types);
|
|
6539
|
+
}
|
|
6540
|
+
const slots = node.slots;
|
|
6541
|
+
if (slots && typeof slots === "object") {
|
|
6542
|
+
for (const slotInstances of Object.values(slots)) {
|
|
6543
|
+
if (!Array.isArray(slotInstances)) continue;
|
|
6544
|
+
for (const instance of slotInstances) {
|
|
6545
|
+
if (instance && typeof instance === "object") {
|
|
6546
|
+
this.extractBlockTypesFromNode(instance, types);
|
|
6547
|
+
}
|
|
6548
|
+
}
|
|
6549
|
+
}
|
|
6550
|
+
}
|
|
6551
|
+
}
|
|
6552
|
+
extractBlockTypesFromParameters(params, types) {
|
|
6553
|
+
for (const param of Object.values(params)) {
|
|
6554
|
+
if (!param || typeof param !== "object") continue;
|
|
6555
|
+
const p = param;
|
|
6556
|
+
if (p.type === "$block" && Array.isArray(p.value)) {
|
|
6557
|
+
for (const item of p.value) {
|
|
6558
|
+
if (item && typeof item === "object") {
|
|
6559
|
+
const blockType = item.type;
|
|
6560
|
+
if (typeof blockType === "string") {
|
|
6561
|
+
types.add(blockType);
|
|
6562
|
+
}
|
|
6563
|
+
}
|
|
6564
|
+
}
|
|
6565
|
+
}
|
|
6566
|
+
}
|
|
6567
|
+
}
|
|
6457
6568
|
isUsed(contentTypeId, usedTypeIds, strict) {
|
|
6458
6569
|
if (strict) {
|
|
6459
6570
|
return usedTypeIds.has(contentTypeId);
|
|
@@ -6484,6 +6595,9 @@ function createRemoveUnusedContentTypesCommand() {
|
|
|
6484
6595
|
rootDir: options.rootDir,
|
|
6485
6596
|
contentTypesDir: options.contentTypesDir,
|
|
6486
6597
|
entriesDir: options.entriesDir,
|
|
6598
|
+
compositionsDir: options.compositionsDir,
|
|
6599
|
+
compositionPatternsDir: options.compositionPatternsDir,
|
|
6600
|
+
componentPatternsDir: options.componentPatternsDir,
|
|
6487
6601
|
whatIf: options.whatIf ?? false,
|
|
6488
6602
|
strict: options.strict ?? false
|
|
6489
6603
|
});
|
|
@@ -6494,10 +6608,390 @@ function createRemoveUnusedContentTypesCommand() {
|
|
|
6494
6608
|
return command;
|
|
6495
6609
|
}
|
|
6496
6610
|
|
|
6611
|
+
// src/cli/commands/remove-unused-component-types.ts
|
|
6612
|
+
import { Command as Command18 } from "commander";
|
|
6613
|
+
|
|
6614
|
+
// src/core/services/unused-component-type-remover.service.ts
|
|
6615
|
+
var UnusedComponentTypeRemoverService = class {
|
|
6616
|
+
constructor(fileSystem, logger) {
|
|
6617
|
+
this.fileSystem = fileSystem;
|
|
6618
|
+
this.logger = logger;
|
|
6619
|
+
}
|
|
6620
|
+
async remove(options) {
|
|
6621
|
+
const {
|
|
6622
|
+
rootDir,
|
|
6623
|
+
componentsDir,
|
|
6624
|
+
compositionsDir,
|
|
6625
|
+
compositionPatternsDir,
|
|
6626
|
+
componentPatternsDir,
|
|
6627
|
+
whatIf,
|
|
6628
|
+
strict
|
|
6629
|
+
} = options;
|
|
6630
|
+
const componentsDirFull = this.fileSystem.resolvePath(rootDir, componentsDir);
|
|
6631
|
+
const ctDirExists = await this.fileSystem.fileExists(componentsDirFull);
|
|
6632
|
+
if (!ctDirExists) {
|
|
6633
|
+
this.logger.warn(`Components directory not found: ${componentsDir} \u2014 nothing to do`);
|
|
6634
|
+
return { totalComponentTypes: 0, removedComponentTypes: 0, retainedComponentTypes: 0 };
|
|
6635
|
+
}
|
|
6636
|
+
const componentFiles = await this.fileSystem.findFiles(componentsDirFull, "*.{json,yaml,yml}");
|
|
6637
|
+
if (componentFiles.length === 0) {
|
|
6638
|
+
this.logger.warn("No component files found \u2014 nothing to do");
|
|
6639
|
+
return { totalComponentTypes: 0, removedComponentTypes: 0, retainedComponentTypes: 0 };
|
|
6640
|
+
}
|
|
6641
|
+
this.logger.info(`Found ${componentFiles.length} component file(s)`);
|
|
6642
|
+
const componentMap = /* @__PURE__ */ new Map();
|
|
6643
|
+
for (const filePath of componentFiles) {
|
|
6644
|
+
let comp;
|
|
6645
|
+
try {
|
|
6646
|
+
comp = await this.fileSystem.readFile(filePath);
|
|
6647
|
+
} catch {
|
|
6648
|
+
this.logger.warn(`Could not read component file: ${filePath} \u2014 skipping`);
|
|
6649
|
+
continue;
|
|
6650
|
+
}
|
|
6651
|
+
if (!comp?.id) {
|
|
6652
|
+
this.logger.warn(`Component file missing id: ${filePath} \u2014 skipping`);
|
|
6653
|
+
continue;
|
|
6654
|
+
}
|
|
6655
|
+
componentMap.set(filePath, { filePath, id: comp.id });
|
|
6656
|
+
}
|
|
6657
|
+
const usedTypeIds = /* @__PURE__ */ new Set();
|
|
6658
|
+
await this.collectTypesFromDirectory(
|
|
6659
|
+
this.fileSystem.resolvePath(rootDir, compositionsDir),
|
|
6660
|
+
usedTypeIds,
|
|
6661
|
+
"compositions"
|
|
6662
|
+
);
|
|
6663
|
+
await this.collectTypesFromDirectory(
|
|
6664
|
+
this.fileSystem.resolvePath(rootDir, compositionPatternsDir),
|
|
6665
|
+
usedTypeIds,
|
|
6666
|
+
"composition patterns"
|
|
6667
|
+
);
|
|
6668
|
+
await this.collectTypesFromDirectory(
|
|
6669
|
+
this.fileSystem.resolvePath(rootDir, componentPatternsDir),
|
|
6670
|
+
usedTypeIds,
|
|
6671
|
+
"component patterns"
|
|
6672
|
+
);
|
|
6673
|
+
await this.collectAllowedComponents(componentFiles, usedTypeIds);
|
|
6674
|
+
this.logger.info(`Found ${usedTypeIds.size} referenced component type(s) across all sources`);
|
|
6675
|
+
let removedComponentTypes = 0;
|
|
6676
|
+
let retainedComponentTypes = 0;
|
|
6677
|
+
for (const { filePath, id } of componentMap.values()) {
|
|
6678
|
+
const isUsed = this.isUsed(id, usedTypeIds, strict);
|
|
6679
|
+
if (isUsed) {
|
|
6680
|
+
retainedComponentTypes++;
|
|
6681
|
+
} else {
|
|
6682
|
+
const relPath = this.fileSystem.joinPath(
|
|
6683
|
+
componentsDir,
|
|
6684
|
+
this.fileSystem.getBasename(filePath)
|
|
6685
|
+
);
|
|
6686
|
+
this.logger.action(whatIf, "DELETE", relPath);
|
|
6687
|
+
if (!whatIf) {
|
|
6688
|
+
this.fileSystem.deleteFile(filePath);
|
|
6689
|
+
}
|
|
6690
|
+
removedComponentTypes++;
|
|
6691
|
+
}
|
|
6692
|
+
}
|
|
6693
|
+
return {
|
|
6694
|
+
totalComponentTypes: componentMap.size,
|
|
6695
|
+
removedComponentTypes,
|
|
6696
|
+
retainedComponentTypes
|
|
6697
|
+
};
|
|
6698
|
+
}
|
|
6699
|
+
async collectTypesFromDirectory(dirPath, usedTypeIds, label) {
|
|
6700
|
+
const dirExists = await this.fileSystem.fileExists(dirPath);
|
|
6701
|
+
if (!dirExists) {
|
|
6702
|
+
return;
|
|
6703
|
+
}
|
|
6704
|
+
let files;
|
|
6705
|
+
try {
|
|
6706
|
+
files = await this.fileSystem.findFiles(dirPath, "**/*.{json,yaml,yml}");
|
|
6707
|
+
} catch {
|
|
6708
|
+
return;
|
|
6709
|
+
}
|
|
6710
|
+
if (files.length === 0) {
|
|
6711
|
+
return;
|
|
6712
|
+
}
|
|
6713
|
+
this.logger.info(`Scanning ${files.length} file(s) in ${label}`);
|
|
6714
|
+
for (const filePath of files) {
|
|
6715
|
+
let composition;
|
|
6716
|
+
try {
|
|
6717
|
+
composition = await this.fileSystem.readFile(filePath);
|
|
6718
|
+
} catch {
|
|
6719
|
+
this.logger.warn(`Could not read file: ${filePath} \u2014 skipping`);
|
|
6720
|
+
continue;
|
|
6721
|
+
}
|
|
6722
|
+
if (!composition?.composition) continue;
|
|
6723
|
+
this.collectTypesFromTree(composition.composition, usedTypeIds);
|
|
6724
|
+
}
|
|
6725
|
+
}
|
|
6726
|
+
collectTypesFromTree(node, usedTypeIds) {
|
|
6727
|
+
if (node.type) {
|
|
6728
|
+
usedTypeIds.add(node.type);
|
|
6729
|
+
}
|
|
6730
|
+
if (node.slots) {
|
|
6731
|
+
for (const slotInstances of Object.values(node.slots)) {
|
|
6732
|
+
if (!Array.isArray(slotInstances)) continue;
|
|
6733
|
+
for (const instance of slotInstances) {
|
|
6734
|
+
if (instance && typeof instance === "object") {
|
|
6735
|
+
this.collectTypesFromTree(
|
|
6736
|
+
instance,
|
|
6737
|
+
usedTypeIds
|
|
6738
|
+
);
|
|
6739
|
+
}
|
|
6740
|
+
}
|
|
6741
|
+
}
|
|
6742
|
+
}
|
|
6743
|
+
}
|
|
6744
|
+
async collectAllowedComponents(componentFiles, usedTypeIds) {
|
|
6745
|
+
for (const filePath of componentFiles) {
|
|
6746
|
+
let comp;
|
|
6747
|
+
try {
|
|
6748
|
+
comp = await this.fileSystem.readFile(filePath);
|
|
6749
|
+
} catch {
|
|
6750
|
+
continue;
|
|
6751
|
+
}
|
|
6752
|
+
if (!comp?.slots) continue;
|
|
6753
|
+
for (const slot of comp.slots) {
|
|
6754
|
+
if (!slot.allowedComponents) continue;
|
|
6755
|
+
for (const allowedType of slot.allowedComponents) {
|
|
6756
|
+
usedTypeIds.add(allowedType);
|
|
6757
|
+
}
|
|
6758
|
+
}
|
|
6759
|
+
}
|
|
6760
|
+
}
|
|
6761
|
+
isUsed(componentId, usedTypeIds, strict) {
|
|
6762
|
+
if (strict) {
|
|
6763
|
+
return usedTypeIds.has(componentId);
|
|
6764
|
+
}
|
|
6765
|
+
const lower = componentId.toLowerCase();
|
|
6766
|
+
for (const used of usedTypeIds) {
|
|
6767
|
+
if (used.toLowerCase() === lower) return true;
|
|
6768
|
+
}
|
|
6769
|
+
return false;
|
|
6770
|
+
}
|
|
6771
|
+
};
|
|
6772
|
+
|
|
6773
|
+
// src/cli/commands/remove-unused-component-types.ts
|
|
6774
|
+
function createRemoveUnusedComponentTypesCommand() {
|
|
6775
|
+
const command = new Command18("remove-unused-component-types");
|
|
6776
|
+
command.description(
|
|
6777
|
+
"Removes component definition files that are not referenced in any composition, pattern, or allowedComponents list."
|
|
6778
|
+
).action(async (_opts, cmd) => {
|
|
6779
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
6780
|
+
const options = {
|
|
6781
|
+
...globalOpts
|
|
6782
|
+
};
|
|
6783
|
+
const logger = new Logger();
|
|
6784
|
+
logger.info(`rootDir: ${options.rootDir}`);
|
|
6785
|
+
logger.info(`componentsDir: ${options.componentsDir}`);
|
|
6786
|
+
logger.info(`compositionsDir: ${options.compositionsDir}`);
|
|
6787
|
+
logger.info(`compositionPatternsDir: ${options.compositionPatternsDir}`);
|
|
6788
|
+
logger.info(`componentPatternsDir: ${options.componentPatternsDir}`);
|
|
6789
|
+
const fileSystem = new FileSystemService();
|
|
6790
|
+
const service = new UnusedComponentTypeRemoverService(fileSystem, logger);
|
|
6791
|
+
const result = await service.remove({
|
|
6792
|
+
rootDir: options.rootDir,
|
|
6793
|
+
componentsDir: options.componentsDir,
|
|
6794
|
+
compositionsDir: options.compositionsDir,
|
|
6795
|
+
compositionPatternsDir: options.compositionPatternsDir,
|
|
6796
|
+
componentPatternsDir: options.componentPatternsDir,
|
|
6797
|
+
whatIf: options.whatIf ?? false,
|
|
6798
|
+
strict: options.strict ?? false
|
|
6799
|
+
});
|
|
6800
|
+
logger.success(
|
|
6801
|
+
`Removed ${result.removedComponentTypes} unused component type(s). ${result.retainedComponentTypes} component type(s) retained.`
|
|
6802
|
+
);
|
|
6803
|
+
});
|
|
6804
|
+
return command;
|
|
6805
|
+
}
|
|
6806
|
+
|
|
6807
|
+
// src/cli/commands/generate-missing-project-map-nodes.ts
|
|
6808
|
+
import { Command as Command19 } from "commander";
|
|
6809
|
+
|
|
6810
|
+
// src/core/services/generate-missing-project-map-nodes.service.ts
|
|
6811
|
+
var GenerateMissingProjectMapNodesService = class {
|
|
6812
|
+
constructor(fileSystem, logger) {
|
|
6813
|
+
this.fileSystem = fileSystem;
|
|
6814
|
+
this.logger = logger;
|
|
6815
|
+
}
|
|
6816
|
+
async generate(options) {
|
|
6817
|
+
const { rootDir, projectMapNodesDir, compositionsDir, rootContentTypes, whatIf, strict } = options;
|
|
6818
|
+
const nodesDirFull = this.fileSystem.resolvePath(rootDir, projectMapNodesDir);
|
|
6819
|
+
const nodesDirExists = await this.fileSystem.fileExists(nodesDirFull);
|
|
6820
|
+
if (!nodesDirExists) {
|
|
6821
|
+
this.logger.warn(`Project map nodes directory not found: ${projectMapNodesDir} \u2014 nothing to do`);
|
|
6822
|
+
return { generatedCount: 0, alreadyCoveredCount: 0 };
|
|
6823
|
+
}
|
|
6824
|
+
const nodeFiles = await this.fileSystem.findFiles(nodesDirFull, "**/*.{json,yaml,yml}");
|
|
6825
|
+
if (nodeFiles.length === 0) {
|
|
6826
|
+
this.logger.warn("No project map node files found \u2014 nothing to do");
|
|
6827
|
+
return { generatedCount: 0, alreadyCoveredCount: 0 };
|
|
6828
|
+
}
|
|
6829
|
+
this.logger.info(`Loaded ${nodeFiles.length} project map node(s)`);
|
|
6830
|
+
const allNodes = [];
|
|
6831
|
+
for (const filePath of nodeFiles) {
|
|
6832
|
+
let node;
|
|
6833
|
+
try {
|
|
6834
|
+
node = await this.fileSystem.readFile(filePath);
|
|
6835
|
+
} catch {
|
|
6836
|
+
this.logger.warn(`Could not read project map node file: ${filePath} \u2014 skipping`);
|
|
6837
|
+
continue;
|
|
6838
|
+
}
|
|
6839
|
+
if (!node?.path) {
|
|
6840
|
+
this.logger.warn(`Project map node missing path field: ${filePath} \u2014 skipping`);
|
|
6841
|
+
continue;
|
|
6842
|
+
}
|
|
6843
|
+
allNodes.push({ filePath, node });
|
|
6844
|
+
}
|
|
6845
|
+
const existingPaths = new Set(allNodes.map(({ node }) => node.path));
|
|
6846
|
+
const compositionsDirFull = this.fileSystem.resolvePath(rootDir, compositionsDir);
|
|
6847
|
+
const compositionIdToType = /* @__PURE__ */ new Map();
|
|
6848
|
+
const compositionsDirExists = await this.fileSystem.fileExists(compositionsDirFull);
|
|
6849
|
+
if (!compositionsDirExists) {
|
|
6850
|
+
this.logger.warn(`Compositions directory not found: ${compositionsDir} \u2014 no source nodes will match`);
|
|
6851
|
+
} else {
|
|
6852
|
+
const compositionFiles = await this.fileSystem.findFiles(
|
|
6853
|
+
compositionsDirFull,
|
|
6854
|
+
"**/*.{json,yaml,yml}"
|
|
6855
|
+
);
|
|
6856
|
+
for (const filePath of compositionFiles) {
|
|
6857
|
+
let comp;
|
|
6858
|
+
try {
|
|
6859
|
+
comp = await this.fileSystem.readFile(filePath);
|
|
6860
|
+
} catch {
|
|
6861
|
+
this.logger.warn(`Could not read composition file: ${filePath} \u2014 skipping`);
|
|
6862
|
+
continue;
|
|
6863
|
+
}
|
|
6864
|
+
if (comp?.composition?._id && comp?.composition?.type) {
|
|
6865
|
+
compositionIdToType.set(comp.composition._id, comp.composition.type);
|
|
6866
|
+
}
|
|
6867
|
+
}
|
|
6868
|
+
}
|
|
6869
|
+
const sourceNodes = allNodes.filter(({ node }) => {
|
|
6870
|
+
if (!node.compositionId) return false;
|
|
6871
|
+
const rootType = compositionIdToType.get(node.compositionId);
|
|
6872
|
+
if (!rootType) {
|
|
6873
|
+
this.logger.warn(
|
|
6874
|
+
`No composition found for compositionId "${node.compositionId}" (node path: ${node.path}) \u2014 skipping`
|
|
6875
|
+
);
|
|
6876
|
+
return false;
|
|
6877
|
+
}
|
|
6878
|
+
return this.matchesContentType(rootType, rootContentTypes, strict);
|
|
6879
|
+
});
|
|
6880
|
+
this.logger.info(`${sourceNodes.length} node(s) match root content types`);
|
|
6881
|
+
const ancestorPaths = /* @__PURE__ */ new Set();
|
|
6882
|
+
for (const { node } of sourceNodes) {
|
|
6883
|
+
for (const ancestor of this.computeAncestorPaths(node.path)) {
|
|
6884
|
+
ancestorPaths.add(ancestor);
|
|
6885
|
+
}
|
|
6886
|
+
}
|
|
6887
|
+
const missingPaths = [];
|
|
6888
|
+
let alreadyCoveredCount = 0;
|
|
6889
|
+
for (const ancestorPath of ancestorPaths) {
|
|
6890
|
+
if (existingPaths.has(ancestorPath)) {
|
|
6891
|
+
alreadyCoveredCount++;
|
|
6892
|
+
} else {
|
|
6893
|
+
missingPaths.push(ancestorPath);
|
|
6894
|
+
}
|
|
6895
|
+
}
|
|
6896
|
+
this.logger.info(
|
|
6897
|
+
`Collected ${ancestorPaths.size} unique ancestor path(s), ${alreadyCoveredCount} already covered`
|
|
6898
|
+
);
|
|
6899
|
+
let generatedCount = 0;
|
|
6900
|
+
for (const missingPath of missingPaths) {
|
|
6901
|
+
const fileName = this.pathToFileName(missingPath);
|
|
6902
|
+
const nodeId = this.pathToNodeId(missingPath);
|
|
6903
|
+
const destFilePath = this.fileSystem.resolvePath(nodesDirFull, fileName);
|
|
6904
|
+
const relPath = this.fileSystem.joinPath(projectMapNodesDir, fileName);
|
|
6905
|
+
this.logger.action(whatIf, "CREATE", `${relPath} (path: ${missingPath}, type: group)`);
|
|
6906
|
+
if (!whatIf) {
|
|
6907
|
+
const newNode = {
|
|
6908
|
+
id: nodeId,
|
|
6909
|
+
path: missingPath,
|
|
6910
|
+
type: "group"
|
|
6911
|
+
};
|
|
6912
|
+
await this.fileSystem.writeFile(destFilePath, newNode);
|
|
6913
|
+
}
|
|
6914
|
+
generatedCount++;
|
|
6915
|
+
}
|
|
6916
|
+
return { generatedCount, alreadyCoveredCount };
|
|
6917
|
+
}
|
|
6918
|
+
computeAncestorPaths(nodePath) {
|
|
6919
|
+
const segments = nodePath.split("/").filter((s) => s.length > 0);
|
|
6920
|
+
const ancestors = [];
|
|
6921
|
+
ancestors.push("/");
|
|
6922
|
+
for (let i = 1; i < segments.length; i++) {
|
|
6923
|
+
ancestors.push("/" + segments.slice(0, i).join("/"));
|
|
6924
|
+
}
|
|
6925
|
+
return ancestors;
|
|
6926
|
+
}
|
|
6927
|
+
pathToNodeId(nodePath) {
|
|
6928
|
+
if (nodePath === "/") return "pmn-group-root";
|
|
6929
|
+
const slug = nodePath.split("/").filter((s) => s.length > 0).join("-");
|
|
6930
|
+
return `pmn-group-${slug}`;
|
|
6931
|
+
}
|
|
6932
|
+
pathToFileName(nodePath) {
|
|
6933
|
+
if (nodePath === "/") return "root-node.yaml";
|
|
6934
|
+
const slug = nodePath.split("/").filter((s) => s.length > 0).join("-");
|
|
6935
|
+
return `${slug}-node.yaml`;
|
|
6936
|
+
}
|
|
6937
|
+
matchesContentType(rootType, rootContentTypes, strict) {
|
|
6938
|
+
for (const ct of rootContentTypes) {
|
|
6939
|
+
if (strict) {
|
|
6940
|
+
if (rootType === ct) return true;
|
|
6941
|
+
} else {
|
|
6942
|
+
if (rootType.toLowerCase() === ct.toLowerCase()) return true;
|
|
6943
|
+
}
|
|
6944
|
+
}
|
|
6945
|
+
return false;
|
|
6946
|
+
}
|
|
6947
|
+
};
|
|
6948
|
+
|
|
6949
|
+
// src/cli/commands/generate-missing-project-map-nodes.ts
|
|
6950
|
+
function createGenerateMissingProjectMapNodesCommand() {
|
|
6951
|
+
const command = new Command19("generate-missing-project-map-nodes");
|
|
6952
|
+
command.description(
|
|
6953
|
+
"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."
|
|
6954
|
+
).option(
|
|
6955
|
+
"--rootContentTypes <types>",
|
|
6956
|
+
'Pipe-separated list of root component types identifying the leaf-page compositions whose ancestor paths should be filled in (e.g. "BlogPost|NewsArticle")'
|
|
6957
|
+
).hook("preAction", (thisCommand) => {
|
|
6958
|
+
const opts = thisCommand.opts();
|
|
6959
|
+
const requiredOptions = [{ name: "rootContentTypes", flag: "--rootContentTypes" }];
|
|
6960
|
+
const missing = requiredOptions.filter((opt) => !opts[opt.name]).map((opt) => opt.flag);
|
|
6961
|
+
if (missing.length > 0) {
|
|
6962
|
+
console.error(`error: missing required options: ${missing.join(", ")}`);
|
|
6963
|
+
process.exit(1);
|
|
6964
|
+
}
|
|
6965
|
+
}).action(async (opts, cmd) => {
|
|
6966
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
6967
|
+
const options = {
|
|
6968
|
+
...globalOpts,
|
|
6969
|
+
rootContentTypes: opts.rootContentTypes
|
|
6970
|
+
};
|
|
6971
|
+
const logger = new Logger();
|
|
6972
|
+
logger.info(`rootContentTypes: ${options.rootContentTypes}`);
|
|
6973
|
+
const rootContentTypes = options.rootContentTypes.split("|").map((t) => t.trim()).filter(Boolean);
|
|
6974
|
+
const fileSystem = new FileSystemService();
|
|
6975
|
+
const service = new GenerateMissingProjectMapNodesService(fileSystem, logger);
|
|
6976
|
+
const result = await service.generate({
|
|
6977
|
+
rootDir: options.rootDir,
|
|
6978
|
+
projectMapNodesDir: options.projectMapNodesDir,
|
|
6979
|
+
compositionsDir: options.compositionsDir,
|
|
6980
|
+
rootContentTypes,
|
|
6981
|
+
whatIf: options.whatIf ?? false,
|
|
6982
|
+
strict: options.strict ?? false
|
|
6983
|
+
});
|
|
6984
|
+
logger.success(
|
|
6985
|
+
`Created ${result.generatedCount} missing project map node(s). ${result.alreadyCoveredCount} ancestor path(s) already existed.`
|
|
6986
|
+
);
|
|
6987
|
+
});
|
|
6988
|
+
return command;
|
|
6989
|
+
}
|
|
6990
|
+
|
|
6497
6991
|
// package.json
|
|
6498
6992
|
var package_default = {
|
|
6499
6993
|
name: "@uniformdev/transformer",
|
|
6500
|
-
version: "1.1.
|
|
6994
|
+
version: "1.1.40",
|
|
6501
6995
|
description: "CLI tool for transforming Uniform.dev serialization files offline",
|
|
6502
6996
|
type: "module",
|
|
6503
6997
|
bin: {
|
|
@@ -6566,7 +7060,7 @@ var package_default = {
|
|
|
6566
7060
|
};
|
|
6567
7061
|
|
|
6568
7062
|
// src/cli/index.ts
|
|
6569
|
-
var program = new
|
|
7063
|
+
var program = new Command20();
|
|
6570
7064
|
var appVersion = package_default.version;
|
|
6571
7065
|
console.error(`uniform-transform v${appVersion}`);
|
|
6572
7066
|
program.name("uniform-transform").description("CLI tool for transforming Uniform.dev serialization files offline").version(appVersion);
|
|
@@ -6596,5 +7090,7 @@ program.addCommand(createAddContentTypeFieldCommand());
|
|
|
6596
7090
|
program.addCommand(createFlattenBlockFieldCommand());
|
|
6597
7091
|
program.addCommand(createRemoveOrphanEntriesCommand());
|
|
6598
7092
|
program.addCommand(createRemoveUnusedContentTypesCommand());
|
|
7093
|
+
program.addCommand(createRemoveUnusedComponentTypesCommand());
|
|
7094
|
+
program.addCommand(createGenerateMissingProjectMapNodesCommand());
|
|
6599
7095
|
program.parse();
|
|
6600
7096
|
//# sourceMappingURL=index.js.map
|