Skip to content

Commit 1a1a606

Browse files
ivanowlstronaut
authored andcommitted
fix: clear non-exist workspace in package-lock.json with npm install
1 parent 3474ec3 commit 1a1a606

File tree

2 files changed

+129
-0
lines changed

2 files changed

+129
-0
lines changed

workspaces/arborist/lib/shrinkwrap.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ const {
4343
stat,
4444
writeFile,
4545
} = require('node:fs/promises')
46+
const { existsSync } = require('node:fs')
4647

4748
const { resolve, basename, relative } = require('node:path')
4849
const specFromLock = require('./spec-from-lock.js')
@@ -927,7 +928,18 @@ class Shrinkwrap {
927928
if (node === this.tree || node.isRoot || node.location === '') {
928929
continue
929930
}
931+
930932
const loc = relpath(this.path, node.path)
933+
934+
// If the node is extraneous and is a workspace that no longer exists on disk, remove it from the workspaces array and skip adding to lockfile
935+
if (node.extraneous && root.workspaces?.length && !existsSync(node.path)) {
936+
const wsIndex = root.workspaces.findIndex(ws => ws === loc)
937+
if (wsIndex > -1) {
938+
root.workspaces.splice(wsIndex, 1)
939+
continue
940+
}
941+
}
942+
931943
this.data.packages[loc] = Shrinkwrap.metaFromNode(
932944
node,
933945
this.path,

workspaces/arborist/test/shrinkwrap.js

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1722,3 +1722,120 @@ t.test('setting lockfileVersion from the file contents', async t => {
17221722
}
17231723
})
17241724
})
1725+
1726+
t.test('removes deleted extraneous workspaces from lockfile', async t => {
1727+
// Simulate a workspace that was removed from disk but still in lockfile
1728+
const path = t.testdir({
1729+
'package.json': JSON.stringify({
1730+
name: 'root',
1731+
version: '1.0.0',
1732+
workspaces: ['packages/*'],
1733+
}),
1734+
'package-lock.json': JSON.stringify({
1735+
name: 'root',
1736+
version: '1.0.0',
1737+
lockfileVersion: 3,
1738+
requires: true,
1739+
packages: {
1740+
'': {
1741+
name: 'root',
1742+
version: '1.0.0',
1743+
workspaces: ['packages/*'],
1744+
},
1745+
'packages/a': {
1746+
name: 'a',
1747+
version: '1.0.0',
1748+
},
1749+
'packages/deleted-ws': {
1750+
name: 'deleted-ws',
1751+
version: '1.0.0',
1752+
},
1753+
},
1754+
}),
1755+
packages: {
1756+
a: {
1757+
'package.json': JSON.stringify({
1758+
name: 'a',
1759+
version: '1.0.0',
1760+
}),
1761+
},
1762+
// Note: 'deleted-ws' folder does NOT exist on disk
1763+
},
1764+
})
1765+
1766+
const arb = new Arborist({ path })
1767+
const tree = await arb.loadActual()
1768+
await calcDepFlags(tree)
1769+
1770+
// The deleted workspace should be marked extraneous
1771+
const deletedWs = tree.children.get('packages/deleted-ws')
1772+
t.notOk(deletedWs, 'deleted workspace should not be in tree children')
1773+
1774+
// Now commit the lockfile
1775+
const lockData = tree.meta.commit()
1776+
1777+
// The deleted workspace should be removed from lockfile packages
1778+
t.notOk(lockData.packages['packages/deleted-ws'],
1779+
'deleted extraneous workspace should be removed from lockfile')
1780+
// But the existing workspace should remain
1781+
t.ok(lockData.packages['packages/a'],
1782+
'existing workspace should remain in lockfile')
1783+
})
1784+
1785+
t.test('keeps extraneous workspaces that exist on disk', async t => {
1786+
// Simulate a workspace that exists on disk but is extraneous (e.g., not matched by the workspaces glob pattern)
1787+
const path = t.testdir({
1788+
'package.json': JSON.stringify({
1789+
name: 'root',
1790+
version: '1.0.0',
1791+
workspaces: ['packages/a'], // Only 'a' is in workspaces
1792+
}),
1793+
'package-lock.json': JSON.stringify({
1794+
name: 'root',
1795+
version: '1.0.0',
1796+
lockfileVersion: 3,
1797+
requires: true,
1798+
packages: {
1799+
'': {
1800+
name: 'root',
1801+
version: '1.0.0',
1802+
workspaces: ['packages/a', 'packages/b'],
1803+
},
1804+
'packages/a': {
1805+
name: 'a',
1806+
version: '1.0.0',
1807+
},
1808+
'packages/b': {
1809+
name: 'b',
1810+
version: '1.0.0',
1811+
},
1812+
},
1813+
}),
1814+
packages: {
1815+
a: {
1816+
'package.json': JSON.stringify({
1817+
name: 'a',
1818+
version: '1.0.0',
1819+
}),
1820+
},
1821+
b: {
1822+
// This workspace exists on disk but is extraneous (not in workspaces config)
1823+
'package.json': JSON.stringify({
1824+
name: 'b',
1825+
version: '1.0.0',
1826+
}),
1827+
},
1828+
},
1829+
})
1830+
1831+
const arb = new Arborist({ path })
1832+
const tree = await arb.loadActual()
1833+
await calcDepFlags(tree)
1834+
1835+
// Now commit the lockfile
1836+
const lockData = tree.meta.commit()
1837+
1838+
// Both workspaces should remain since 'b' exists on disk
1839+
t.ok(lockData.packages['packages/a'],
1840+
'configured workspace should remain in lockfile')
1841+
})

0 commit comments

Comments
 (0)