| 112 | }); |
| 113 | |
| 114 | export function deduplicateLockfileYaml(yamlContent: string): { |
| 115 | deduplicatedContent: string; |
| 116 | duplicatesRemoved: number; |
| 117 | } { |
| 118 | // Parse using parseDocument to access the raw YAML structure |
| 119 | const doc = YAML.parseDocument(yamlContent); |
| 120 | let duplicatesRemoved = 0; |
| 121 | |
| 122 | // Remove duplicate keys from the YAML document structure |
| 123 | if (doc.contents && YAML.isMap(doc.contents)) { |
| 124 | const checksums = doc.contents.get('checksums'); |
| 125 | if (checksums && YAML.isMap(checksums)) { |
| 126 | // Iterate through each path pattern hash |
| 127 | for (const pathItem of checksums.items) { |
| 128 | if (YAML.isMap(pathItem.value)) { |
| 129 | // Track key positions - last occurrence wins |
| 130 | const keyPositions = new Map<string, number[]>(); |
| 131 | |
| 132 | // First pass: collect all positions for each key |
| 133 | for (let i = 0; i < pathItem.value.items.length; i++) { |
| 134 | const translationItem = pathItem.value.items[i]; |
| 135 | const key = String(YAML.isScalar(translationItem.key) ? translationItem.key.value : translationItem.key); |
| 136 | |
| 137 | if (!keyPositions.has(key)) { |
| 138 | keyPositions.set(key, []); |
| 139 | } |
| 140 | keyPositions.get(key)!.push(i); |
| 141 | } |
| 142 | |
| 143 | // Second pass: identify duplicates to remove (all but the last occurrence) |
| 144 | const indicesToRemove: number[] = []; |
| 145 | for (const positions of keyPositions.values()) { |
| 146 | if (positions.length > 1) { |
| 147 | // Keep the last occurrence, remove all earlier ones |
| 148 | indicesToRemove.push(...positions.slice(0, -1)); |
| 149 | duplicatesRemoved += positions.length - 1; |
| 150 | } |
| 151 | } |
| 152 | |
| 153 | // Remove items in reverse order to maintain correct indices |
| 154 | indicesToRemove.sort((a, b) => b - a); |
| 155 | for (const index of indicesToRemove) { |
| 156 | pathItem.value.items.splice(index, 1); |
| 157 | } |
| 158 | } |
| 159 | } |
| 160 | } |
| 161 | } |
| 162 | |
| 163 | // Convert to JavaScript object (which is now clean) |
| 164 | const cleanedData = doc.toJSON(); |
| 165 | // Create a new document from the cleaned data |
| 166 | const cleanDoc = new YAML.Document(cleanedData); |
| 167 | const deduplicatedContent = cleanDoc.toString(); |
| 168 | |
| 169 | return { |
| 170 | deduplicatedContent, |
| 171 | duplicatesRemoved, |