diff --git a/packages/cli/src/lib/versioning/Lockfile.test.ts b/packages/cli/src/lib/versioning/Lockfile.test.ts index 8209ef1f23..b65d2c0fbe 100644 --- a/packages/cli/src/lib/versioning/Lockfile.test.ts +++ b/packages/cli/src/lib/versioning/Lockfile.test.ts @@ -416,30 +416,30 @@ d@^1: it('should diff two legacy lockfiles', async () => { expect(lockfileLegacyA.diff(lockfileLegacyB)).toEqual({ added: [ - { name: 'b', range: '4' }, - { name: 'd', range: '^1' }, + { name: 'b', range: '3' }, + { name: 'c', range: '^1' }, ], changed: [ { name: 'a', range: '^1' }, { name: 'b', range: '2.0.x' }, ], removed: [ - { name: 'b', range: '3' }, - { name: 'c', range: '^1' }, + { name: 'b', range: '4' }, + { name: 'd', range: '^1' }, ], }); expect(lockfileLegacyB.diff(lockfileLegacyA)).toEqual({ added: [ - { name: 'b', range: '3' }, - { name: 'c', range: '^1' }, + { name: 'b', range: '4' }, + { name: 'd', range: '^1' }, ], changed: [ { name: 'a', range: '^1' }, { name: 'b', range: '2.0.x' }, ], removed: [ - { name: 'b', range: '4' }, - { name: 'd', range: '^1' }, + { name: 'b', range: '3' }, + { name: 'c', range: '^1' }, ], }); }); @@ -447,30 +447,30 @@ d@^1: it('should diff two modern lockfiles', async () => { expect(lockfileModernA.diff(lockfileModernB)).toEqual({ added: [ - { name: 'b', range: '4' }, - { name: 'd', range: '^1' }, + { name: 'b', range: '3' }, + { name: 'c', range: '^1' }, ], changed: [ { name: 'a', range: '^1' }, { name: 'b', range: '2.0.x' }, ], removed: [ - { name: 'b', range: '3' }, - { name: 'c', range: '^1' }, + { name: 'b', range: '4' }, + { name: 'd', range: '^1' }, ], }); expect(lockfileModernB.diff(lockfileModernA)).toEqual({ added: [ - { name: 'b', range: '3' }, - { name: 'c', range: '^1' }, + { name: 'b', range: '4' }, + { name: 'd', range: '^1' }, ], changed: [ { name: 'a', range: '^1' }, { name: 'b', range: '2.0.x' }, ], removed: [ - { name: 'b', range: '4' }, - { name: 'd', range: '^1' }, + { name: 'b', range: '3' }, + { name: 'c', range: '^1' }, ], }); }); @@ -478,30 +478,30 @@ d@^1: it('should diff legacy and modern lockfiles', async () => { expect(lockfileLegacyA.diff(lockfileModernB)).toEqual({ added: [ - { name: 'b', range: '4' }, - { name: 'd', range: '^1' }, + { name: 'b', range: '3' }, + { name: 'c', range: '^1' }, ], changed: [ { name: 'a', range: '^1' }, { name: 'b', range: '2.0.x' }, ], removed: [ - { name: 'b', range: '3' }, - { name: 'c', range: '^1' }, + { name: 'b', range: '4' }, + { name: 'd', range: '^1' }, ], }); expect(lockfileLegacyB.diff(lockfileModernA)).toEqual({ added: [ - { name: 'b', range: '3' }, - { name: 'c', range: '^1' }, + { name: 'b', range: '4' }, + { name: 'd', range: '^1' }, ], changed: [ { name: 'a', range: '^1' }, { name: 'b', range: '2.0.x' }, ], removed: [ - { name: 'b', range: '4' }, - { name: 'd', range: '^1' }, + { name: 'b', range: '3' }, + { name: 'c', range: '^1' }, ], }); }); @@ -509,30 +509,30 @@ d@^1: it('should diff modern and legacy lockfiles', async () => { expect(lockfileModernA.diff(lockfileLegacyB)).toEqual({ added: [ - { name: 'b', range: '4' }, - { name: 'd', range: '^1' }, + { name: 'b', range: '3' }, + { name: 'c', range: '^1' }, ], changed: [ { name: 'a', range: '^1' }, { name: 'b', range: '2.0.x' }, ], removed: [ - { name: 'b', range: '3' }, - { name: 'c', range: '^1' }, + { name: 'b', range: '4' }, + { name: 'd', range: '^1' }, ], }); expect(lockfileModernB.diff(lockfileLegacyA)).toEqual({ added: [ - { name: 'b', range: '3' }, - { name: 'c', range: '^1' }, + { name: 'b', range: '4' }, + { name: 'd', range: '^1' }, ], changed: [ { name: 'a', range: '^1' }, { name: 'b', range: '2.0.x' }, ], removed: [ - { name: 'b', range: '4' }, - { name: 'd', range: '^1' }, + { name: 'b', range: '3' }, + { name: 'c', range: '^1' }, ], }); }); diff --git a/packages/cli/src/lib/versioning/Lockfile.ts b/packages/cli/src/lib/versioning/Lockfile.ts index d156c9498d..8f4bd6af19 100644 --- a/packages/cli/src/lib/versioning/Lockfile.ts +++ b/packages/cli/src/lib/versioning/Lockfile.ts @@ -38,6 +38,17 @@ type LockfileQueryEntry = { dataKey: string; }; +type LockfileDiffEntry = { + name: string; + range: string; +}; + +type LockfileDiff = { + added: LockfileDiffEntry[]; + changed: LockfileDiffEntry[]; + removed: LockfileDiffEntry[]; +}; + /** Entries that have an invalid version range, for example an npm tag */ type AnalyzeResultInvalidRange = { name: string; @@ -328,56 +339,60 @@ export class Lockfile { } } - diff(newLockfile: Lockfile) { + /** + * Diff with another lockfile, returning entries that have been + * added, changed, and removed compared to the other lockfile. + */ + diff(otherLockfile: Lockfile): LockfileDiff { const diff = { added: new Array<{ name: string; range: string }>(), - removed: new Array<{ name: string; range: string }>(), changed: new Array<{ name: string; range: string }>(), + removed: new Array<{ name: string; range: string }>(), }; - // Keeps track of packages that only exist in the old lockfile + // Keeps track of packages that only exist in this lockfile const remainingOldNames = new Set(this.packages.keys()); - for (const [name, newQueries] of newLockfile.packages) { + for (const [name, otherQueries] of otherLockfile.packages) { remainingOldNames.delete(name); - const oldQueries = this.packages.get(name); - // If the packages doesn't exist in the old lockfile, add all entries - if (!oldQueries) { - diff.added.push(...newQueries.map(q => ({ name, range: q.range }))); + const thisQueries = this.packages.get(name); + // If the packages doesn't exist in this lockfile, add all entries + if (!thisQueries) { + diff.removed.push(...otherQueries.map(q => ({ name, range: q.range }))); continue; } - const remainingOldRanges = new Set(oldQueries.map(q => q.range)); + const remainingOldRanges = new Set(thisQueries.map(q => q.range)); - for (const newQuery of newQueries) { - remainingOldRanges.delete(newQuery.range); + for (const otherQuery of otherQueries) { + remainingOldRanges.delete(otherQuery.range); - const oldQuery = oldQueries.find(q => q.range === newQuery.range); - if (!oldQuery) { - diff.added.push({ name, range: newQuery.range }); + const thisQuery = thisQueries.find(q => q.range === otherQuery.range); + if (!thisQuery) { + diff.removed.push({ name, range: otherQuery.range }); continue; } - const newPkg = newLockfile.data[newQuery.dataKey]; - const oldPkg = this.data[oldQuery.dataKey]; - if (newPkg && oldPkg) { - const oldCheck = oldPkg.integrity || oldPkg.checksum; - const newCheck = newPkg.integrity || newPkg.checksum; - if (!oldCheck || !newCheck || oldCheck !== newCheck) { - diff.changed.push({ name, range: newQuery.range }); + const otherPkg = otherLockfile.data[otherQuery.dataKey]; + const thisPkg = this.data[thisQuery.dataKey]; + if (otherPkg && thisPkg) { + const thisCheck = thisPkg.integrity || thisPkg.checksum; + const otherCheck = otherPkg.integrity || otherPkg.checksum; + if (!thisCheck || !otherCheck || thisCheck !== otherCheck) { + diff.changed.push({ name, range: otherQuery.range }); } } } - for (const oldRange of remainingOldRanges) { - diff.removed.push({ name, range: oldRange }); + for (const thisRange of remainingOldRanges) { + diff.added.push({ name, range: thisRange }); } } for (const name of remainingOldNames) { const queries = this.packages.get(name) ?? []; - diff.removed.push(...queries.map(q => ({ name, range: q.range }))); + diff.added.push(...queries.map(q => ({ name, range: q.range }))); } return diff;