From b390c75a6f60feda360116dbfa3835b11e26613f Mon Sep 17 00:00:00 2001 From: Zoltan Kochan Date: Tue, 28 Dec 2021 23:34:02 +0200 Subject: [PATCH] fix: injection of subdependencies (#4167) --- .changeset/strong-pugs-invite.md | 5 ++ .../core/test/install/injectLocalPackages.ts | 69 +++++++++++++++++-- .../src/getNonDevWantedDependencies.ts | 5 +- 3 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 .changeset/strong-pugs-invite.md diff --git a/.changeset/strong-pugs-invite.md b/.changeset/strong-pugs-invite.md new file mode 100644 index 0000000000..2142b30e9e --- /dev/null +++ b/.changeset/strong-pugs-invite.md @@ -0,0 +1,5 @@ +--- +"@pnpm/resolve-dependencies": patch +--- + +Injected subdependencies should be hard linked as well. So if `button` is injected into `card` and `card` is injected into `page`, then both `button` and `card` should be injected into `page`. diff --git a/packages/core/test/install/injectLocalPackages.ts b/packages/core/test/install/injectLocalPackages.ts index 005068fcba..02a832b97f 100644 --- a/packages/core/test/install/injectLocalPackages.ts +++ b/packages/core/test/install/injectLocalPackages.ts @@ -1,3 +1,4 @@ +import fs from 'fs' import path from 'path' import assertProject from '@pnpm/assert-project' import { MutatedProject, mutateModules } from '@pnpm/core' @@ -17,22 +18,39 @@ test('inject local packages', async () => { 'dep-of-pkg-with-1-dep': '100.0.0', }, peerDependencies: { - 'is-positive': '1.0.0', + 'is-positive': '>=1.0.0', }, } const project2Manifest = { name: 'project-2', version: '1.0.0', dependencies: { - 'is-positive': '1.0.0', 'project-1': 'workspace:1.0.0', }, + devDependencies: { + 'is-positive': '1.0.0', + }, dependenciesMeta: { 'project-1': { injected: true, }, }, } + const project3Manifest = { + name: 'project-3', + version: '1.0.0', + dependencies: { + 'project-2': 'workspace:1.0.0', + }, + devDependencies: { + 'is-positive': '2.0.0', + }, + dependenciesMeta: { + 'project-2': { + injected: true, + }, + }, + } const projects = preparePackages([ { location: 'project-1', @@ -42,6 +60,10 @@ test('inject local packages', async () => { location: 'project-2', package: project2Manifest, }, + { + location: 'project-3', + package: project3Manifest, + }, ]) const importers: MutatedProject[] = [ @@ -57,6 +79,12 @@ test('inject local packages', async () => { mutation: 'install', rootDir: path.resolve('project-2'), }, + { + buildIndex: 0, + manifest: project3Manifest, + mutation: 'install', + rootDir: path.resolve('project-3'), + }, ] const workspacePackages = { 'project-1': { @@ -71,6 +99,12 @@ test('inject local packages', async () => { manifest: project2Manifest, }, }, + 'project-3': { + '1.0.0': { + dir: path.resolve('project-3'), + manifest: project2Manifest, + }, + }, } await mutateModules(importers, await testDefaults({ workspacePackages, @@ -83,6 +117,11 @@ test('inject local packages', async () => { await projects['project-2'].has('is-positive') await projects['project-2'].has('project-1') + await projects['project-3'].has('is-positive') + await projects['project-3'].has('project-2') + + expect(fs.readdirSync('node_modules/.pnpm').length).toBe(8) + const rootModules = assertProject(process.cwd()) { const lockfile = await rootModules.readLockfile() @@ -100,7 +139,7 @@ test('inject local packages', async () => { name: 'project-1', version: '1.0.0', peerDependencies: { - 'is-positive': '1.0.0', + 'is-positive': '>=1.0.0', }, dependencies: { 'is-negative': '1.0.0', @@ -108,11 +147,26 @@ test('inject local packages', async () => { }, dev: false, }) + expect(lockfile.packages['file:project-2_is-positive@2.0.0']).toEqual({ + resolution: { + directory: 'project-2', + type: 'directory', + }, + id: 'file:project-2', + name: 'project-2', + version: '1.0.0', + dependencies: { + 'project-1': 'file:project-1_is-positive@2.0.0', + }, + transitivePeerDependencies: ['is-positive'], + dev: false, + }) } await rimraf('node_modules') await rimraf('project-1/node_modules') await rimraf('project-2/node_modules') + await rimraf('project-3/node_modules') await mutateModules(importers, await testDefaults({ frozenLockfile: true, @@ -126,7 +180,12 @@ test('inject local packages', async () => { await projects['project-2'].has('is-positive') await projects['project-2'].has('project-1') - // The injected project is updated when on of its dependencies needs to be updated + await projects['project-3'].has('is-positive') + await projects['project-3'].has('project-2') + + expect(fs.readdirSync('node_modules/.pnpm').length).toBe(8) + + // The injected project is updated when one of its dependencies needs to be updated importers[0].manifest.dependencies!['is-negative'] = '2.0.0' await mutateModules(importers, await testDefaults({ workspacePackages })) { @@ -145,7 +204,7 @@ test('inject local packages', async () => { name: 'project-1', version: '1.0.0', peerDependencies: { - 'is-positive': '1.0.0', + 'is-positive': '>=1.0.0', }, dependencies: { 'is-negative': '2.0.0', diff --git a/packages/resolve-dependencies/src/getNonDevWantedDependencies.ts b/packages/resolve-dependencies/src/getNonDevWantedDependencies.ts index d3005bfeeb..bdcf376300 100644 --- a/packages/resolve-dependencies/src/getNonDevWantedDependencies.ts +++ b/packages/resolve-dependencies/src/getNonDevWantedDependencies.ts @@ -1,4 +1,4 @@ -import { Dependencies, DependencyManifest } from '@pnpm/types' +import { Dependencies, DependencyManifest, DependenciesMeta } from '@pnpm/types' export interface WantedDependency { alias: string @@ -16,6 +16,7 @@ export default function getNonDevWantedDependencies (pkg: DependencyManifest) { return getWantedDependenciesFromGivenSet( filterDeps({ ...pkg.optionalDependencies, ...pkg.dependencies }), { + dependenciesMeta: pkg.dependenciesMeta ?? {}, devDependencies: {}, optionalDependencies: pkg.optionalDependencies ?? {}, } @@ -27,12 +28,14 @@ function getWantedDependenciesFromGivenSet ( opts: { devDependencies: Dependencies optionalDependencies: Dependencies + dependenciesMeta: DependenciesMeta } ): WantedDependency[] { if (!deps) return [] return Object.keys(deps).map((alias) => ({ alias, dev: !!opts.devDependencies[alias], + injected: opts.dependenciesMeta[alias]?.injected, optional: !!opts.optionalDependencies[alias], pref: deps[alias], }))