Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions workspaces/arborist/lib/arborist/reify.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,16 +110,23 @@ module.exports = cls => class Reifier extends cls {
await this[_loadTrees](options)

const oldTree = this.idealTree
let swappedToIsolated = false
if (linked) {
// swap out the tree with the isolated tree
// this is currently technical debt which will be resolved in a refactor
// of Node/Link trees
log.warn('reify', 'The "linked" install strategy is EXPERIMENTAL and may contain bugs.')
this.idealTree = await this[_createIsolatedTree]()
// Workspace-filtered installs are incompatible with the isolated tree
// which uses plain objects with Array children and a stub inventory.
// Use the normal reify path for workspace installs.
if (!this.options.workspaces.length) {
this.idealTree = await this[_createIsolatedTree]()
swappedToIsolated = true
}
}
await this[_diffTrees]()
await this.#reifyPackages()
if (linked) {
if (swappedToIsolated) {
// swap back in the idealTree
// so that the lockfile is preserved
this.idealTree = oldTree
Expand Down
7 changes: 6 additions & 1 deletion workspaces/arborist/lib/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,12 @@ class Node {
return false
}
for (const edge of this.edgesIn) {
if (!npa(edge.spec).registry) {
try {
if (!npa(edge.spec).registry) {
return false
}
} catch {
// unsupported spec types (e.g. link:) are not registry deps
return false
}
}
Expand Down
41 changes: 41 additions & 0 deletions workspaces/arborist/test/isolated-mode.js
Original file line number Diff line number Diff line change
Expand Up @@ -1637,6 +1637,47 @@ tap.test('bins are installed', async t => {
t.ok(binFromBarToWhich)
})

tap.test('workspace-filtered install with linked strategy does not crash', async t => {
const graph = {
registry: [
{ name: 'which', version: '1.0.0' },
{ name: 'isexe', version: '1.0.0' },
],
root: {
name: 'dog', version: '1.2.3',
},
workspaces: [
{ name: 'bar', version: '1.0.0', dependencies: { which: '1.0.0' } },
{ name: 'baz', version: '1.0.0', dependencies: { isexe: '1.0.0' } },
],
}

const { dir, registry } = await getRepo(graph)

const cache = fs.mkdtempSync(`${getTempDir()}/test-`)

// First, do a full linked install
const arb1 = new Arborist({ path: dir, registry, packumentCache: new Map(), cache })
await arb1.reify({ installStrategy: 'linked' })

// Now do a workspace-filtered install - this used to crash with
// "Cannot read properties of null (reading 'package')" because the
// isolated tree's plain objects are incompatible with workspace filtering.
const arb2 = new Arborist({
path: dir,
registry,
packumentCache: new Map(),
cache,
installStrategy: 'linked',
workspaces: ['bar'],
})
await arb2.reify({ installStrategy: 'linked' })

// The workspace's dependency should still be resolvable
const whichPath = resolvePackage('which', path.join(dir, 'packages', 'bar'))
t.ok(whichPath, 'bar workspace can still resolve which')
})

function setupRequire (cwd) {
return function requireChain (...chain) {
return chain.reduce((path, name) => {
Expand Down
Loading