diff --git a/node_modules/@npmcli/arborist/lib/arborist/build-ideal-tree.js b/node_modules/@npmcli/arborist/lib/arborist/build-ideal-tree.js index 1f36776351f83..ae9965e2e9286 100644 --- a/node_modules/@npmcli/arborist/lib/arborist/build-ideal-tree.js +++ b/node_modules/@npmcli/arborist/lib/arborist/build-ideal-tree.js @@ -1210,7 +1210,7 @@ This is a one-time fix-up, please be patient... continue const peer = virtualRoot.children.get(peerEdge.name) const peerPlaced = this[_placeDep]( - peer, newDep, peerEdge, peerEntryEdge || edge) + peer, newDep, peerEdge, peerEntryEdge || edge, peerPath) placed.push(...peerPlaced) } @@ -1262,11 +1262,21 @@ This is a one-time fix-up, please be patient... [_canPlaceDep] (dep, target, edge, peerEntryEdge = null, peerPath = []) { const entryEdge = peerEntryEdge || edge const source = this[_peerSetSource].get(dep) - const isSource = target === source const { isRoot, isWorkspace } = source || {} const isMine = isRoot || isWorkspace + // XXX - Useful testing thingie right here. + // This assertion is commented out because we don't want to blow up user + // projects if it happens. But the peerEntryEdge should *always* be a + // non-peer dependency, or a peer dependency from the root node. When we + // get spurious ERESOLVE errors, or *don't* get ERESOLVE errors when we + // should, try uncommenting this and seeing if it fails, because it means + // we got off track somehow. + // if (peerEntryEdge && peerEntryEdge.peer && !peerEntryEdge.from.isTop) { + // throw new Error('this should never happen') + // } + if (target.children.has(edge.name)) { const current = target.children.get(edge.name) @@ -1420,18 +1430,14 @@ This is a one-time fix-up, please be patient... if (!dep.parent || peerEntryEdge && peerPath.includes(dep)) return ret + const entryEdge = peerEntryEdge || edge peerPath = [...peerPath, dep] - for (const peer of dep.parent.children.values()) { - if (peer === dep) - continue - // we only have to pick the first one, because ALL peer edgesIn will - // be checked before we decide to accept an existing dep in the tree - const peerEdge = [...peer.edgesIn].find(e => e.peer && e !== edge) - if (!peerEdge) + for (const peerEdge of dep.edgesOut.values()) { + if (!peerEdge.peer || !peerEdge.to) continue - - const canPlacePeer = this[_canPlaceDep](peer, target, peerEdge, edge, peerPath) + const peer = peerEdge.to + const canPlacePeer = this[_canPlaceDep](peer, target, peerEdge, entryEdge, peerPath) if (canPlacePeer !== CONFLICT) continue diff --git a/node_modules/@npmcli/arborist/lib/arborist/rebuild.js b/node_modules/@npmcli/arborist/lib/arborist/rebuild.js index b610045c0655e..f4f3ca12b7011 100644 --- a/node_modules/@npmcli/arborist/lib/arborist/rebuild.js +++ b/node_modules/@npmcli/arborist/lib/arborist/rebuild.js @@ -16,6 +16,8 @@ const { const boolEnv = b => b ? '1' : '' const sortNodes = (a, b) => (a.depth - b.depth) || a.path.localeCompare(b.path) +const _build = Symbol('build') +const _resetQueues = Symbol('resetQueues') const _rebuildBundle = Symbol('rebuildBundle') const _ignoreScripts = Symbol('ignoreScripts') const _binLinks = Symbol('binLinks') @@ -51,12 +53,7 @@ module.exports = cls => class Builder extends cls { this[_ignoreScripts] = !!ignoreScripts this[_scriptShell] = scriptShell this[_rebuildBundle] = !!rebuildBundle - this[_queues] = { - preinstall: [], - install: [], - postinstall: [], - bin: [], - } + this[_resetQueues]() this[_oldMeta] = null } @@ -75,19 +72,62 @@ module.exports = cls => class Builder extends cls { if (!nodes) nodes = (await this.loadActual()).inventory.values() + // separates links nodes so that it can run + // prepare scripts and link bins in the expected order process.emit('time', 'build') + const depNodes = new Set() + const linkNodes = new Set() + for (const node of nodes) { + // we skip the target nodes to that workspace in order to make sure + // we only run lifecycle scripts / place bin links once per workspace + if (node.isLink) + linkNodes.add(node) + else + depNodes.add(node) + } + + await this[_build](depNodes, {}) + + if (linkNodes.size) { + this[_resetQueues]() + await this[_build](linkNodes, { type: 'links' }) + } + + process.emit('timeEnd', 'build') + } + + [_resetQueues] () { + this[_queues] = { + preinstall: [], + install: [], + postinstall: [], + prepare: [], + bin: [], + } + } + + async [_build] (nodes, { type = 'deps' }) { + process.emit('time', `build:${type}`) await this[_buildQueues](nodes) if (!this[_ignoreScripts]) await this[_runScripts]('preinstall') - if (this[_binLinks]) + if (this[_binLinks] && type !== 'links') await this[_linkAllBins]() if (!this[_ignoreScripts]) { await this[_runScripts]('install') await this[_runScripts]('postinstall') } - process.emit('timeEnd', 'build') + // links should also run prepare scripts and only link bins after that + if (type === 'links') { + await this[_runScripts]('prepare') + + if (this[_binLinks]) + await this[_linkAllBins]() + } + + process.emit('timeEnd', `build:${type}`) } async [_buildQueues] (nodes) { @@ -121,8 +161,8 @@ module.exports = cls => class Builder extends cls { for (const node of queue) { const { package: { bin, scripts = {} } } = node - const { preinstall, install, postinstall } = scripts - const tests = { bin, preinstall, install, postinstall } + const { preinstall, install, postinstall, prepare } = scripts + const tests = { bin, preinstall, install, postinstall, prepare } for (const [key, has] of Object.entries(tests)) { if (has) this[_queues][key].push(node) @@ -156,8 +196,8 @@ module.exports = cls => class Builder extends cls { const { package: pkg, hasInstallScript } = node const { gypfile, bin, scripts = {} } = pkg - const { preinstall, install, postinstall } = scripts - const anyScript = preinstall || install || postinstall + const { preinstall, install, postinstall, prepare } = scripts + const anyScript = preinstall || install || postinstall || prepare if (!refreshed && !anyScript && (hasInstallScript || this[_oldMeta])) { // we either have an old metadata (and thus might have scripts) // or we have an indication that there's install scripts (but @@ -183,7 +223,7 @@ module.exports = cls => class Builder extends cls { !preinstall && await isNodeGypPackage(node.path) - if (bin || preinstall || install || postinstall || isGyp) { + if (bin || preinstall || install || postinstall || prepare || isGyp) { if (bin) await this[_checkBins](node) if (isGyp) { @@ -212,7 +252,7 @@ module.exports = cls => class Builder extends cls { devOptional, package: pkg, location, - } = node + } = node.target || node // skip any that we know we'll be deleting if (this[_trashList].has(path)) diff --git a/node_modules/@npmcli/arborist/package.json b/node_modules/@npmcli/arborist/package.json index 4172b9323f4d5..580419a1034c4 100644 --- a/node_modules/@npmcli/arborist/package.json +++ b/node_modules/@npmcli/arborist/package.json @@ -1,6 +1,6 @@ { "name": "@npmcli/arborist", - "version": "1.0.2", + "version": "1.0.3", "description": "Manage node_modules trees", "dependencies": { "@npmcli/installed-package-contents": "^1.0.5", diff --git a/package-lock.json b/package-lock.json index 6e28afbbc1d86..270d996ae55a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -77,7 +77,7 @@ ], "license": "Artistic-2.0", "dependencies": { - "@npmcli/arborist": "^1.0.2", + "@npmcli/arborist": "^1.0.3", "@npmcli/ci-detect": "^1.2.0", "@npmcli/config": "^1.2.1", "@npmcli/run-script": "^1.7.0", @@ -381,9 +381,9 @@ } }, "node_modules/@npmcli/arborist": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-1.0.2.tgz", - "integrity": "sha512-YBlU3HcRI7Q1dVQa4ljf/ifKxbAZELe+e66NP/vNFrv6pv4oVrOOkBrMCC/0tareDKKpxtHoGS5oVFwCF8ZzGg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-1.0.3.tgz", + "integrity": "sha512-eRyDsmcWISxK/hz3RyqyEJAAMG7qF76c0ckaw3nRgU+1L05kaA30COKsrXlyB/G0gV6uaiVvPgZJknm70hMaYg==", "inBundle": true, "dependencies": { "@npmcli/installed-package-contents": "^1.0.5", @@ -8477,9 +8477,9 @@ } }, "@npmcli/arborist": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-1.0.2.tgz", - "integrity": "sha512-YBlU3HcRI7Q1dVQa4ljf/ifKxbAZELe+e66NP/vNFrv6pv4oVrOOkBrMCC/0tareDKKpxtHoGS5oVFwCF8ZzGg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-1.0.3.tgz", + "integrity": "sha512-eRyDsmcWISxK/hz3RyqyEJAAMG7qF76c0ckaw3nRgU+1L05kaA30COKsrXlyB/G0gV6uaiVvPgZJknm70hMaYg==", "requires": { "@npmcli/installed-package-contents": "^1.0.5", "@npmcli/map-workspaces": "^1.0.1", diff --git a/package.json b/package.json index 5c8dcdb8e3d12..b49ef2f54902f 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "./package.json": "./package.json" }, "dependencies": { - "@npmcli/arborist": "^1.0.2", + "@npmcli/arborist": "^1.0.3", "@npmcli/ci-detect": "^1.2.0", "@npmcli/config": "^1.2.1", "@npmcli/run-script": "^1.7.0",