mirror of
https://github.com/pnpm/pnpm.git
synced 2026-04-28 02:53:15 -04:00
fix(resolve-dependencies): a sequence of injected deps via the file protocol (#4415)
This commit is contained in:
50
.changeset/selfish-boxes-fry.md
Normal file
50
.changeset/selfish-boxes-fry.md
Normal file
@@ -0,0 +1,50 @@
|
||||
---
|
||||
"@pnpm/resolve-dependencies": patch
|
||||
"pnpm": patch
|
||||
---
|
||||
|
||||
The location of an injected directory dependency should be correctly located, when there is a chain of local dependencies (declared via the `file:` protocol`).
|
||||
|
||||
The next scenario was not working prior to the fix. There are 3 projects in the same folder: foo, bar, qar.
|
||||
|
||||
`foo/package.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "foo",
|
||||
"dependencies": {
|
||||
"bar": "file:../bar"
|
||||
},
|
||||
"dependenciesMeta": {
|
||||
"bar": {
|
||||
"injected": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`bar/package.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "bar",
|
||||
"dependencies": {
|
||||
"qar": "file:../qar"
|
||||
},
|
||||
"dependenciesMeta": {
|
||||
"qar": {
|
||||
"injected": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`qar/package.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "qar"
|
||||
}
|
||||
```
|
||||
|
||||
Related PR: [#4415](https://github.com/pnpm/pnpm/pull/4415).
|
||||
@@ -5,6 +5,7 @@ import { MutatedProject, mutateModules } from '@pnpm/core'
|
||||
import { preparePackages } from '@pnpm/prepare'
|
||||
import rimraf from '@zkochan/rimraf'
|
||||
import pathExists from 'path-exists'
|
||||
import { sync as writeJsonFile } from 'write-json-file'
|
||||
import { testDefaults } from '../utils'
|
||||
|
||||
test('inject local packages', async () => {
|
||||
@@ -224,6 +225,224 @@ test('inject local packages', async () => {
|
||||
}
|
||||
})
|
||||
|
||||
test('inject local packages declared via file protocol', async () => {
|
||||
const project1Manifest = {
|
||||
name: 'project-1',
|
||||
version: '1.0.0',
|
||||
dependencies: {
|
||||
'is-negative': '1.0.0',
|
||||
},
|
||||
devDependencies: {
|
||||
'dep-of-pkg-with-1-dep': '100.0.0',
|
||||
},
|
||||
peerDependencies: {
|
||||
'is-positive': '>=1.0.0',
|
||||
},
|
||||
}
|
||||
const project2Manifest = {
|
||||
name: 'project-2',
|
||||
version: '1.0.0',
|
||||
dependencies: {
|
||||
'project-1': 'file:../project-1',
|
||||
},
|
||||
devDependencies: {
|
||||
'is-positive': '1.0.0',
|
||||
},
|
||||
dependenciesMeta: {
|
||||
'project-1': {
|
||||
injected: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
const project3Manifest = {
|
||||
name: 'project-3',
|
||||
version: '1.0.0',
|
||||
dependencies: {
|
||||
'project-2': 'file:../project-2',
|
||||
},
|
||||
devDependencies: {
|
||||
'is-positive': '2.0.0',
|
||||
},
|
||||
dependenciesMeta: {
|
||||
'project-2': {
|
||||
injected: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
const projects = preparePackages([
|
||||
{
|
||||
location: 'project-1',
|
||||
package: project1Manifest,
|
||||
},
|
||||
{
|
||||
location: 'project-2',
|
||||
package: project2Manifest,
|
||||
},
|
||||
{
|
||||
location: 'project-3',
|
||||
package: project3Manifest,
|
||||
},
|
||||
])
|
||||
|
||||
const importers: MutatedProject[] = [
|
||||
{
|
||||
buildIndex: 0,
|
||||
manifest: project1Manifest,
|
||||
mutation: 'install',
|
||||
rootDir: path.resolve('project-1'),
|
||||
},
|
||||
{
|
||||
buildIndex: 0,
|
||||
manifest: project2Manifest,
|
||||
mutation: 'install',
|
||||
rootDir: path.resolve('project-2'),
|
||||
},
|
||||
{
|
||||
buildIndex: 0,
|
||||
manifest: project3Manifest,
|
||||
mutation: 'install',
|
||||
rootDir: path.resolve('project-3'),
|
||||
},
|
||||
]
|
||||
const workspacePackages = {
|
||||
'project-1': {
|
||||
'1.0.0': {
|
||||
dir: path.resolve('project-1'),
|
||||
manifest: project1Manifest,
|
||||
},
|
||||
},
|
||||
'project-2': {
|
||||
'1.0.0': {
|
||||
dir: path.resolve('project-2'),
|
||||
manifest: project2Manifest,
|
||||
},
|
||||
},
|
||||
'project-3': {
|
||||
'1.0.0': {
|
||||
dir: path.resolve('project-3'),
|
||||
manifest: project2Manifest,
|
||||
},
|
||||
},
|
||||
}
|
||||
await mutateModules(importers, await testDefaults({
|
||||
workspacePackages,
|
||||
}))
|
||||
|
||||
await projects['project-1'].has('is-negative')
|
||||
await projects['project-1'].has('dep-of-pkg-with-1-dep')
|
||||
await projects['project-1'].hasNot('is-positive')
|
||||
|
||||
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()
|
||||
expect(lockfile.importers['project-2'].dependenciesMeta).toEqual({
|
||||
'project-1': {
|
||||
injected: true,
|
||||
},
|
||||
})
|
||||
expect(lockfile.packages['file:project-1_is-positive@1.0.0']).toEqual({
|
||||
resolution: {
|
||||
directory: 'project-1',
|
||||
type: 'directory',
|
||||
},
|
||||
id: 'file:project-1',
|
||||
name: 'project-1',
|
||||
version: '1.0.0',
|
||||
peerDependencies: {
|
||||
'is-positive': '>=1.0.0',
|
||||
},
|
||||
dependencies: {
|
||||
'is-negative': '1.0.0',
|
||||
'is-positive': '1.0.0',
|
||||
},
|
||||
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,
|
||||
})
|
||||
|
||||
const modulesState = await rootModules.readModulesManifest()
|
||||
expect(modulesState?.injectedDeps?.['project-1'].length).toEqual(2)
|
||||
expect(modulesState?.injectedDeps?.['project-1'][0]).toContain(`node_modules${path.sep}.pnpm`)
|
||||
expect(modulesState?.injectedDeps?.['project-1'][1]).toContain(`node_modules${path.sep}.pnpm`)
|
||||
}
|
||||
|
||||
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,
|
||||
workspacePackages,
|
||||
}))
|
||||
|
||||
await projects['project-1'].has('is-negative')
|
||||
await projects['project-1'].has('dep-of-pkg-with-1-dep')
|
||||
await projects['project-1'].hasNot('is-positive')
|
||||
|
||||
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)
|
||||
|
||||
// The injected project is updated when one of its dependencies needs to be updated
|
||||
importers[0].manifest.dependencies!['is-negative'] = '2.0.0'
|
||||
writeJsonFile('project-1/package.json', importers[0].manifest)
|
||||
await mutateModules(importers, await testDefaults({ workspacePackages }))
|
||||
{
|
||||
const lockfile = await rootModules.readLockfile()
|
||||
expect(lockfile.importers['project-2'].dependenciesMeta).toEqual({
|
||||
'project-1': {
|
||||
injected: true,
|
||||
},
|
||||
})
|
||||
expect(lockfile.packages['file:project-1_is-positive@1.0.0']).toEqual({
|
||||
resolution: {
|
||||
directory: 'project-1',
|
||||
type: 'directory',
|
||||
},
|
||||
id: 'file:project-1',
|
||||
name: 'project-1',
|
||||
version: '1.0.0',
|
||||
peerDependencies: {
|
||||
'is-positive': '>=1.0.0',
|
||||
},
|
||||
dependencies: {
|
||||
'is-negative': '2.0.0',
|
||||
'is-positive': '1.0.0',
|
||||
},
|
||||
dev: false,
|
||||
})
|
||||
const modulesState = await rootModules.readModulesManifest()
|
||||
expect(modulesState?.injectedDeps?.['project-1'].length).toEqual(2)
|
||||
expect(modulesState?.injectedDeps?.['project-1'][0]).toContain(`node_modules${path.sep}.pnpm`)
|
||||
expect(modulesState?.injectedDeps?.['project-1'][1]).toContain(`node_modules${path.sep}.pnpm`)
|
||||
}
|
||||
})
|
||||
|
||||
test('inject local packages and relink them after build', async () => {
|
||||
const project1Manifest = {
|
||||
name: 'project-1',
|
||||
|
||||
@@ -619,7 +619,12 @@ async function resolveDependency (
|
||||
lockfileDir: ctx.lockfileDir,
|
||||
preferredVersions: options.preferredVersions,
|
||||
preferWorkspacePackages: ctx.preferWorkspacePackages,
|
||||
projectDir: options.currentDepth > 0 ? ctx.lockfileDir : ctx.prefix,
|
||||
projectDir: (
|
||||
options.currentDepth > 0 &&
|
||||
!wantedDependency.pref.startsWith('file:')
|
||||
)
|
||||
? ctx.lockfileDir
|
||||
: ctx.prefix,
|
||||
registry: wantedDependency.alias && pickRegistryForPackage(ctx.registries, wantedDependency.alias, wantedDependency.pref) || ctx.registries.default,
|
||||
// Unfortunately, even when run with --lockfile-only, we need the *real* package.json
|
||||
// so fetching of the tarball cannot be ever avoided. Related issue: https://github.com/pnpm/pnpm/issues/1176
|
||||
|
||||
Reference in New Issue
Block a user