fix: hoisting in a workspace

Direct dependencies of workspace projects should
be hoisted.

close #2315
PR #2322
This commit is contained in:
Zoltan Kochan
2020-02-03 23:23:44 +02:00
committed by GitHub
parent 7859d80cfe
commit 0b14ffbff4
5 changed files with 49 additions and 26 deletions

View File

@@ -33,7 +33,7 @@ export default function filterByImporters (
lockfile,
importerIds,
{ include: opts.include, skipped: opts.skipped },
),
).step,
packages,
{
failOnMissingDependencies: opts.failOnMissingDependencies,

View File

@@ -27,19 +27,33 @@ export default async function hoistByLockfile (
) {
if (!opts.lockfile.packages) return {}
const deps = await getDependencies(
lockfileWalker(
opts.lockfile,
Object.keys(opts.lockfile.importers),
),
0,
{
getIndependentPackageLocation: opts.getIndependentPackageLocation,
lockfileDir: opts.lockfileDir,
registries: opts.registries,
virtualStoreDir: opts.virtualStoreDir,
},
const { directDeps, step } = lockfileWalker(
opts.lockfile,
Object.keys(opts.lockfile.importers),
)
const deps = [
{
absolutePath: '',
children: directDeps
.reduce((acc, dep) => {
if (acc[dep.alias]) return acc
acc[dep.alias] = dp.resolve(opts.registries, dep.relDepPath)
return acc
}, {}),
depth: -1,
location: '',
},
...await getDependencies(
step,
0,
{
getIndependentPackageLocation: opts.getIndependentPackageLocation,
lockfileDir: opts.lockfileDir,
registries: opts.registries,
virtualStoreDir: opts.virtualStoreDir,
},
),
]
const aliasesByDependencyPath = await hoistGraph(deps, opts.lockfile.importers['.'].specifiers, {
dryRun: false,
@@ -92,7 +106,6 @@ async function getDependencies (
location: !independent
? path.join(modules, pkgName)
: await opts.getIndependentPackageLocation!(pkgSnapshot.id || absolutePath, pkgName),
name: pkgName,
})
nextSteps.push(next())
@@ -112,7 +125,6 @@ async function getDependencies (
}
export interface Dependency {
name: string,
location: string,
children: {[alias: string]: string},
depth: number,
@@ -135,7 +147,7 @@ async function hoistGraph (
// sort by depth and then alphabetically
.sort((a, b) => {
const depthDiff = a.depth - b.depth
return depthDiff === 0 ? a.name.localeCompare(b.name) : depthDiff
return depthDiff === 0 ? a.absolutePath.localeCompare(b.absolutePath) : depthDiff
})
// build the alias map and the id map
.map((depNode) => {

View File

@@ -55,6 +55,7 @@ export default function lockfileWalker (
) {
const walked = new Set<string>(opts?.skipped ? Array.from(opts?.skipped) : [])
const entryNodes = [] as string[]
const directDeps = [] as Array<{ alias: string, relDepPath: string }>
importerIds.forEach((importerId) => {
const projectSnapshot = lockfile.importers[importerId]
@@ -63,17 +64,21 @@ export default function lockfileWalker (
...(opts?.include?.dependencies === false ? {} : projectSnapshot.dependencies),
...(opts?.include?.optionalDependencies === false ? {} : projectSnapshot.optionalDependencies),
})
.map(([ pkgName, reference ]) => dp.refToRelative(reference, pkgName))
.filter((nodeId) => nodeId !== null)
.forEach((relDepPath) => {
.forEach(([ pkgName, reference ]) => {
const relDepPath = dp.refToRelative(reference, pkgName)
if (relDepPath === null) return
entryNodes.push(relDepPath as string)
directDeps.push({ alias: pkgName, relDepPath })
})
})
return step({
includeOptionalDependencies: opts?.include?.optionalDependencies !== false,
lockfile,
walked,
}, entryNodes)
return {
directDeps,
step: step({
includeOptionalDependencies: opts?.include?.optionalDependencies !== false,
lockfile,
walked,
}, entryNodes),
}
}
function step (

View File

@@ -245,7 +245,7 @@ async function _rebuild (
optionalDependencies: opts.optional,
},
},
),
).step,
nodesToBuildAndTransitive,
{ pkgsToRebuild: ctx.pkgsToRebuild },
)

View File

@@ -311,7 +311,7 @@ test('should uninstall correctly peer dependencies', async (t) => {
t.throws(() => fs.lstatSync('node_modules/ajv-keywords'), Error, 'symlink to peer dependency is deleted')
})
test('hoist-pattern: only hoists the dependencies of the root workspace package', async (t) => {
test('hoist-pattern: hoist all dependencies to the virtual store node_modules', async (t) => {
const workspaceRootManifest = {
name: 'root',
@@ -355,6 +355,9 @@ test('hoist-pattern: only hoists the dependencies of the root workspace package'
await projects['root'].has('pkg-with-1-dep')
await projects['root'].has('.pnpm/node_modules/dep-of-pkg-with-1-dep')
await projects['root'].has('.pnpm/node_modules/foobar')
await projects['root'].has('.pnpm/node_modules/foo')
await projects['root'].has('.pnpm/node_modules/bar')
await projects['root'].hasNot('foobar')
await projects['root'].hasNot('foo')
await projects['root'].hasNot('bar')
@@ -370,6 +373,9 @@ test('hoist-pattern: only hoists the dependencies of the root workspace package'
await projects['root'].has('pkg-with-1-dep')
await projects['root'].has('.pnpm/node_modules/dep-of-pkg-with-1-dep')
await projects['root'].has('.pnpm/node_modules/foobar')
await projects['root'].has('.pnpm/node_modules/foo')
await projects['root'].has('.pnpm/node_modules/bar')
await projects['root'].hasNot('foobar')
await projects['root'].hasNot('foo')
await projects['root'].hasNot('bar')