( fromTree: PackageFileTree[], toTree: PackageFileTree[], )
| 49 | |
| 50 | /** Compare two file trees and return changes */ |
| 51 | export function compareFileTrees( |
| 52 | fromTree: PackageFileTree[], |
| 53 | toTree: PackageFileTree[], |
| 54 | ): { added: FileChange[]; removed: FileChange[]; modified: FileChange[]; truncated: boolean } { |
| 55 | const fromFiles = flattenTree(fromTree) |
| 56 | const toFiles = flattenTree(toTree) |
| 57 | |
| 58 | const added: FileChange[] = [] |
| 59 | const removed: FileChange[] = [] |
| 60 | const modified: FileChange[] = [] |
| 61 | let truncated = false |
| 62 | const overLimit = () => added.length + removed.length + modified.length >= MAX_FILES_COMPARE |
| 63 | |
| 64 | // Find added and modified files |
| 65 | for (const [path, toNode] of toFiles) { |
| 66 | if (overLimit()) { |
| 67 | truncated = true |
| 68 | break |
| 69 | } |
| 70 | |
| 71 | const fromNode = fromFiles.get(path) |
| 72 | |
| 73 | // Handle directory -> file / file -> directory transitions |
| 74 | if (toNode.type === 'directory') { |
| 75 | if (fromNode?.type === 'file') { |
| 76 | removed.push({ |
| 77 | path, |
| 78 | type: 'removed', |
| 79 | oldSize: fromNode.size, |
| 80 | }) |
| 81 | } |
| 82 | continue |
| 83 | } |
| 84 | |
| 85 | // toNode is file |
| 86 | if (!fromNode) { |
| 87 | // New file |
| 88 | added.push({ |
| 89 | path, |
| 90 | type: 'added', |
| 91 | newSize: toNode.size, |
| 92 | }) |
| 93 | } else if (fromNode.type === 'directory') { |
| 94 | // Path was a directory, now a file -> treat as added file |
| 95 | added.push({ |
| 96 | path, |
| 97 | type: 'added', |
| 98 | newSize: toNode.size, |
| 99 | }) |
| 100 | } else if (fromNode.type === 'file') { |
| 101 | if (hasChanged(fromNode, toNode)) { |
| 102 | modified.push({ |
| 103 | path, |
| 104 | type: 'modified', |
| 105 | oldSize: fromNode.size, |
| 106 | newSize: toNode.size, |
| 107 | }) |
| 108 | } |
no test coverage detected