diff --git a/.changeset/blue-planes-sort.md b/.changeset/blue-planes-sort.md new file mode 100644 index 0000000000..39ad9f7a25 --- /dev/null +++ b/.changeset/blue-planes-sort.md @@ -0,0 +1,5 @@ +--- +"@pnpm/pkg-manager.direct-dep-linker": patch +--- + +Deduping should not fail when node_modules has non-symlinked packages. diff --git a/pkg-manager/core/test/install/dedupeDirectDeps.ts b/pkg-manager/core/test/install/dedupeDirectDeps.ts index 5ea2a58dc1..a23718fa5a 100644 --- a/pkg-manager/core/test/install/dedupeDirectDeps.ts +++ b/pkg-manager/core/test/install/dedupeDirectDeps.ts @@ -19,6 +19,8 @@ test('dedupe direct dependencies', async () => { package: { name: 'project-3' }, }, ]) + fs.mkdirSync('node_modules/foo', { recursive: true }) + fs.writeFileSync('node_modules/foo/package.json', JSON.stringify({ name: 'foo', version: '1.0.0' })) const importers: MutatedProject[] = [ { @@ -85,6 +87,7 @@ test('dedupe direct dependencies', async () => { expect(Array.from(fs.readdirSync('node_modules').sort())).toEqual([ '.modules.yaml', '.pnpm', + 'foo', 'is-negative', 'is-odd', 'is-positive', diff --git a/pkg-manager/direct-dep-linker/src/linkDirectDeps.ts b/pkg-manager/direct-dep-linker/src/linkDirectDeps.ts index d289ac6dc7..6b39d7040f 100644 --- a/pkg-manager/direct-dep-linker/src/linkDirectDeps.ts +++ b/pkg-manager/direct-dep-linker/src/linkDirectDeps.ts @@ -71,7 +71,7 @@ function pathsEqual (path1: string, path2: string) { async function readLinkedDeps (modulesDir: string): Promise { const deps = (await readModulesDir(modulesDir)) ?? [] return Promise.all( - deps.map((alias) => resolveLinkTarget(path.join(modulesDir, alias))) + deps.map((alias) => resolveLinkTargetOrFile(path.join(modulesDir, alias))) ) } @@ -92,11 +92,20 @@ async function readLinkedDepsWithRealLocations (modulesDir: string) { const linkedTo = path.join(modulesDir, alias) return { linkedTo, - linkedFrom: await resolveLinkTarget(linkedTo), + linkedFrom: await resolveLinkTargetOrFile(linkedTo), } })) } +async function resolveLinkTargetOrFile (filePath: string) { + try { + return await resolveLinkTarget(filePath) + } catch (err: any) { // eslint-disable-line @typescript-eslint/no-explicit-any + if (err.code !== 'EINVAL') throw err + return filePath + } +} + async function linkDirectDepsOfProject (project: ProjectToLink) { await Promise.all(project.dependencies.map(async (dep) => { if (dep.isExternalLink) {