mirror of
https://github.com/pnpm/pnpm.git
synced 2025-12-23 23:29:17 -05:00
fix: link overrides should work on non-root workspace projects (#3388)
This commit is contained in:
5
.changeset/chilly-trees-kneel.md
Normal file
5
.changeset/chilly-trees-kneel.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"supi": patch
|
||||
---
|
||||
|
||||
It should be possible to override dependencies with links.
|
||||
5
.changeset/flat-phones-shake.md
Normal file
5
.changeset/flat-phones-shake.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@pnpm/get-context": minor
|
||||
---
|
||||
|
||||
Pass in the location of the project to the `readPackage` hook.
|
||||
5
.changeset/silly-geckos-hide.md
Normal file
5
.changeset/silly-geckos-hide.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@pnpm/types": minor
|
||||
---
|
||||
|
||||
An optional `dir` parameter added to the `ReadPackageHook` function. The `dir` parameter is defined when the hook runs on project manifests and defined the root of the project.
|
||||
@@ -42,7 +42,7 @@
|
||||
"syncpack": "^5.7.11",
|
||||
"ts-jest": "27.0.0-next.11",
|
||||
"ts-node": "^9.1.1",
|
||||
"typescript": "^4.3.0-beta",
|
||||
"typescript": "^4.2.4",
|
||||
"verdaccio": "^5.0.0",
|
||||
"yarn": "^1.22.10"
|
||||
},
|
||||
|
||||
@@ -131,7 +131,7 @@ export default async function getContext<T> (
|
||||
if ((opts.hooks?.readPackage) != null) {
|
||||
for (const project of importersContext.projects) {
|
||||
project.originalManifest = project.manifest
|
||||
project.manifest = opts.hooks.readPackage(R.clone(project.manifest))
|
||||
project.manifest = opts.hooks.readPackage(R.clone(project.manifest), project.rootDir)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
import path from 'path'
|
||||
import { Dependencies, PackageManifest, ReadPackageHook } from '@pnpm/types'
|
||||
import parseWantedDependency from '@pnpm/parse-wanted-dependency'
|
||||
import normalizePath from 'normalize-path'
|
||||
import semver from 'semver'
|
||||
|
||||
export default function (overrides: Record<string, string>): ReadPackageHook {
|
||||
export default function (overrides: Record<string, string>, rootDir: string): ReadPackageHook {
|
||||
const genericVersionOverrides = [] as VersionOverride[]
|
||||
const versionOverrides = [] as VersionOverrideWithParent[]
|
||||
Object.entries(overrides)
|
||||
.forEach(([selector, newPref]) => {
|
||||
let linkTarget: string | undefined
|
||||
if (newPref.startsWith('link:')) {
|
||||
linkTarget = path.join(rootDir, newPref.substring(5))
|
||||
}
|
||||
if (selector.includes('>')) {
|
||||
const [parentSelector, childSelector] = selector.split('>')
|
||||
versionOverrides.push({
|
||||
linkTarget,
|
||||
newPref,
|
||||
parentWantedDependency: parseWantedDependency(parentSelector),
|
||||
wantedDependency: parseWantedDependency(childSelector),
|
||||
@@ -17,18 +24,19 @@ export default function (overrides: Record<string, string>): ReadPackageHook {
|
||||
return
|
||||
}
|
||||
genericVersionOverrides.push({
|
||||
linkTarget,
|
||||
newPref,
|
||||
wantedDependency: parseWantedDependency(selector),
|
||||
} as VersionOverride)
|
||||
})
|
||||
return ((pkg: PackageManifest) => {
|
||||
overrideDepsOfPkg(pkg, versionOverrides.filter(({ parentWantedDependency }) => {
|
||||
return parentWantedDependency.alias === pkg.name && (
|
||||
!parentWantedDependency.pref || semver.satisfies(pkg.version, parentWantedDependency.pref)
|
||||
return ((manifest: PackageManifest, dir?: string) => {
|
||||
overrideDepsOfPkg({ manifest, dir }, versionOverrides.filter(({ parentWantedDependency }) => {
|
||||
return parentWantedDependency.alias === manifest.name && (
|
||||
!parentWantedDependency.pref || semver.satisfies(manifest.version, parentWantedDependency.pref)
|
||||
)
|
||||
}))
|
||||
overrideDepsOfPkg(pkg, genericVersionOverrides)
|
||||
return pkg
|
||||
overrideDepsOfPkg({ manifest, dir }, genericVersionOverrides)
|
||||
return manifest
|
||||
}) as ReadPackageHook
|
||||
}
|
||||
|
||||
@@ -42,6 +50,7 @@ interface VersionOverride {
|
||||
pref?: string
|
||||
}
|
||||
newPref: string
|
||||
linkTarget?: string
|
||||
}
|
||||
|
||||
interface VersionOverrideWithParent extends VersionOverride {
|
||||
@@ -51,27 +60,33 @@ interface VersionOverrideWithParent extends VersionOverride {
|
||||
}
|
||||
}
|
||||
|
||||
function overrideDepsOfPkg (pkg: PackageManifest, versionOverrides: VersionOverride[]) {
|
||||
if (pkg.dependencies != null) overrideDeps(versionOverrides, pkg.dependencies)
|
||||
if (pkg.optionalDependencies != null) overrideDeps(versionOverrides, pkg.optionalDependencies)
|
||||
if (pkg.devDependencies != null) overrideDeps(versionOverrides, pkg.devDependencies)
|
||||
return pkg
|
||||
function overrideDepsOfPkg (
|
||||
{ manifest, dir }: { manifest: PackageManifest, dir: string | undefined },
|
||||
versionOverrides: VersionOverride[]
|
||||
) {
|
||||
if (manifest.dependencies != null) overrideDeps(versionOverrides, manifest.dependencies, dir)
|
||||
if (manifest.optionalDependencies != null) overrideDeps(versionOverrides, manifest.optionalDependencies, dir)
|
||||
if (manifest.devDependencies != null) overrideDeps(versionOverrides, manifest.devDependencies, dir)
|
||||
return manifest
|
||||
}
|
||||
|
||||
function overrideDeps (versionOverrides: VersionOverride[], deps: Dependencies) {
|
||||
function overrideDeps (versionOverrides: VersionOverride[], deps: Dependencies, dir: string | undefined) {
|
||||
for (const versionOverride of versionOverrides) {
|
||||
const actual = deps[versionOverride.wantedDependency.alias]
|
||||
if (
|
||||
actual &&
|
||||
(
|
||||
!versionOverride.wantedDependency.pref ||
|
||||
actual === versionOverride.wantedDependency.pref ||
|
||||
semver.validRange(actual) != null &&
|
||||
semver.validRange(versionOverride.wantedDependency.pref) != null &&
|
||||
semver.subset(actual, versionOverride.wantedDependency.pref)
|
||||
)
|
||||
) {
|
||||
deps[versionOverride.wantedDependency.alias] = versionOverride.newPref
|
||||
if (actual == null) continue
|
||||
if (!isSubRange(versionOverride.wantedDependency.pref, actual)) continue
|
||||
if (versionOverride.linkTarget && dir) {
|
||||
deps[versionOverride.wantedDependency.alias] = `link:${normalizePath(path.relative(dir, versionOverride.linkTarget))}`
|
||||
continue
|
||||
}
|
||||
deps[versionOverride.wantedDependency.alias] = versionOverride.newPref
|
||||
}
|
||||
}
|
||||
|
||||
function isSubRange (superRange: string | undefined, subRange: string) {
|
||||
return !superRange ||
|
||||
subRange === superRange ||
|
||||
semver.validRange(subRange) != null &&
|
||||
semver.validRange(superRange) != null &&
|
||||
semver.subset(subRange, superRange)
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ export async function mutateModules (
|
||||
: undefined
|
||||
const neverBuiltDependencies = rootProjectManifest?.pnpm?.neverBuiltDependencies ?? []
|
||||
if (!R.isEmpty(overrides ?? {})) {
|
||||
const versionsOverrider = createVersionsOverrider(overrides!)
|
||||
const versionsOverrider = createVersionsOverrider(overrides!, opts.lockfileDir)
|
||||
if (opts.hooks.readPackage != null) {
|
||||
opts.hooks.readPackage = R.pipe(
|
||||
opts.hooks.readPackage,
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
import createVersionOverrider from 'supi/lib/install/createVersionsOverrider'
|
||||
|
||||
test('createVersionsOverrider() overrides dependencies of specified packages only', () => {
|
||||
const overrider = createVersionOverrider({
|
||||
'foo@1>bar@^1.2.0': '3.0.0',
|
||||
})
|
||||
expect(overrider({
|
||||
name: 'foo',
|
||||
version: '1.2.0',
|
||||
dependencies: {
|
||||
bar: '^1.2.0',
|
||||
},
|
||||
})).toStrictEqual({
|
||||
name: 'foo',
|
||||
version: '1.2.0',
|
||||
dependencies: {
|
||||
bar: '3.0.0',
|
||||
},
|
||||
})
|
||||
expect(overrider({
|
||||
name: 'foo',
|
||||
version: '2.0.0',
|
||||
dependencies: {
|
||||
bar: '^1.2.0',
|
||||
},
|
||||
})).toStrictEqual({
|
||||
name: 'foo',
|
||||
version: '2.0.0',
|
||||
dependencies: {
|
||||
bar: '^1.2.0',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test('createVersionsOverrider() overrides all types of dependencies', () => {
|
||||
const overrider = createVersionOverrider({
|
||||
foo: '3.0.0',
|
||||
bar: '3.0.0',
|
||||
qar: '3.0.0',
|
||||
})
|
||||
expect(overrider({
|
||||
name: 'foo',
|
||||
version: '1.2.0',
|
||||
dependencies: {
|
||||
foo: '^1.2.0',
|
||||
},
|
||||
optionalDependencies: {
|
||||
bar: '^1.2.0',
|
||||
},
|
||||
devDependencies: {
|
||||
qar: '^1.2.0',
|
||||
},
|
||||
})).toStrictEqual({
|
||||
name: 'foo',
|
||||
version: '1.2.0',
|
||||
dependencies: {
|
||||
foo: '3.0.0',
|
||||
},
|
||||
optionalDependencies: {
|
||||
bar: '3.0.0',
|
||||
},
|
||||
devDependencies: {
|
||||
qar: '3.0.0',
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -1,9 +1,10 @@
|
||||
import path from 'path'
|
||||
import createVersionsOverrider from 'supi/lib/install/createVersionsOverrider'
|
||||
|
||||
test('createVersionsOverrider() matches subranges', () => {
|
||||
const overrider = createVersionsOverrider({
|
||||
'foo@2': '2.12.0',
|
||||
})
|
||||
}, process.cwd())
|
||||
expect(
|
||||
overrider({
|
||||
dependencies: { foo: '^2.10.0' },
|
||||
@@ -15,7 +16,7 @@ test('createVersionsOverrider() does not fail on non-range selectors', () => {
|
||||
const overrider = createVersionsOverrider({
|
||||
'foo@2': '2.12.0',
|
||||
'bar@github:org/bar': '2.12.0',
|
||||
})
|
||||
}, process.cwd())
|
||||
expect(
|
||||
overrider({
|
||||
dependencies: {
|
||||
@@ -30,3 +31,87 @@ test('createVersionsOverrider() does not fail on non-range selectors', () => {
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test('createVersionsOverrider() overrides dependencies of specified packages only', () => {
|
||||
const overrider = createVersionsOverrider({
|
||||
'foo@1>bar@^1.2.0': '3.0.0',
|
||||
}, process.cwd())
|
||||
expect(overrider({
|
||||
name: 'foo',
|
||||
version: '1.2.0',
|
||||
dependencies: {
|
||||
bar: '^1.2.0',
|
||||
},
|
||||
})).toStrictEqual({
|
||||
name: 'foo',
|
||||
version: '1.2.0',
|
||||
dependencies: {
|
||||
bar: '3.0.0',
|
||||
},
|
||||
})
|
||||
expect(overrider({
|
||||
name: 'foo',
|
||||
version: '2.0.0',
|
||||
dependencies: {
|
||||
bar: '^1.2.0',
|
||||
},
|
||||
})).toStrictEqual({
|
||||
name: 'foo',
|
||||
version: '2.0.0',
|
||||
dependencies: {
|
||||
bar: '^1.2.0',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test('createVersionsOverrider() overrides all types of dependencies', () => {
|
||||
const overrider = createVersionsOverrider({
|
||||
foo: '3.0.0',
|
||||
bar: '3.0.0',
|
||||
qar: '3.0.0',
|
||||
}, process.cwd())
|
||||
expect(overrider({
|
||||
name: 'foo',
|
||||
version: '1.2.0',
|
||||
dependencies: {
|
||||
foo: '^1.2.0',
|
||||
},
|
||||
optionalDependencies: {
|
||||
bar: '^1.2.0',
|
||||
},
|
||||
devDependencies: {
|
||||
qar: '^1.2.0',
|
||||
},
|
||||
})).toStrictEqual({
|
||||
name: 'foo',
|
||||
version: '1.2.0',
|
||||
dependencies: {
|
||||
foo: '3.0.0',
|
||||
},
|
||||
optionalDependencies: {
|
||||
bar: '3.0.0',
|
||||
},
|
||||
devDependencies: {
|
||||
qar: '3.0.0',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test('createVersionsOverrider() overrides dependencies with links', () => {
|
||||
const overrider = createVersionsOverrider({
|
||||
qar: 'link:../qar',
|
||||
}, process.cwd())
|
||||
expect(overrider({
|
||||
name: 'foo',
|
||||
version: '1.2.0',
|
||||
dependencies: {
|
||||
qar: '3.0.0',
|
||||
},
|
||||
}, path.resolve('pkg'))).toStrictEqual({
|
||||
name: 'foo',
|
||||
version: '1.2.0',
|
||||
dependencies: {
|
||||
qar: 'link:../../qar',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
@@ -14,6 +14,6 @@ export type IncludedDependencies = {
|
||||
}
|
||||
|
||||
export interface ReadPackageHook {
|
||||
(pkg: PackageManifest): PackageManifest
|
||||
(pkg: ProjectManifest): ProjectManifest
|
||||
(pkg: PackageManifest, dir?: string): PackageManifest
|
||||
(pkg: ProjectManifest, dir?: string): ProjectManifest
|
||||
}
|
||||
|
||||
22
pnpm-lock.yaml
generated
22
pnpm-lock.yaml
generated
@@ -38,7 +38,7 @@ importers:
|
||||
syncpack: ^5.7.11
|
||||
ts-jest: 27.0.0-next.11
|
||||
ts-node: ^9.1.1
|
||||
typescript: ^4.3.0-beta
|
||||
typescript: ^4.2.4
|
||||
verdaccio: ^5.0.0
|
||||
yarn: ^1.22.10
|
||||
devDependencies:
|
||||
@@ -64,9 +64,9 @@ importers:
|
||||
rimraf: 3.0.2
|
||||
shx: 0.3.3
|
||||
syncpack: 5.7.11
|
||||
ts-jest: 27.0.0-next.11_a5546de6b94dcb9edcea3d9b2bcd5fbc
|
||||
ts-node: 9.1.1_typescript@4.3.0-dev.20210423
|
||||
typescript: 4.3.0-dev.20210423
|
||||
ts-jest: 27.0.0-next.11_79fc8990d1d2d58e33fab6b2a5ad4bdc
|
||||
ts-node: 9.1.1_typescript@4.2.4
|
||||
typescript: 4.2.4
|
||||
verdaccio: 5.0.1
|
||||
yarn: 1.22.10
|
||||
|
||||
@@ -9118,7 +9118,7 @@ packages:
|
||||
jest-validate: 27.0.0-next.8
|
||||
micromatch: 4.0.4
|
||||
pretty-format: 27.0.0-next.8
|
||||
ts-node: 9.1.1_typescript@4.3.0-dev.20210423
|
||||
ts-node: 9.1.1_typescript@4.2.4
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- canvas
|
||||
@@ -13127,7 +13127,7 @@ packages:
|
||||
utf8-byte-length: 1.0.4
|
||||
dev: false
|
||||
|
||||
/ts-jest/27.0.0-next.11_a5546de6b94dcb9edcea3d9b2bcd5fbc:
|
||||
/ts-jest/27.0.0-next.11_79fc8990d1d2d58e33fab6b2a5ad4bdc:
|
||||
resolution: {integrity: sha512-MdlskBkkYy2/Q47Wlilf0ZJhRe1kZdP9ul7vOwsICUtS+47aCv47nRMrueDg1VCcOcWT3ZgwZ3XaYXcGFEhcHA==}
|
||||
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
|
||||
hasBin: true
|
||||
@@ -13145,11 +13145,11 @@ packages:
|
||||
make-error: 1.3.6
|
||||
mkdirp: 1.0.4
|
||||
semver: 7.3.5
|
||||
typescript: 4.3.0-dev.20210423
|
||||
typescript: 4.2.4
|
||||
yargs-parser: 20.2.7
|
||||
dev: true
|
||||
|
||||
/ts-node/9.1.1_typescript@4.3.0-dev.20210423:
|
||||
/ts-node/9.1.1_typescript@4.2.4:
|
||||
resolution: {integrity: sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
hasBin: true
|
||||
@@ -13161,7 +13161,7 @@ packages:
|
||||
diff: 4.0.2
|
||||
make-error: 1.3.6
|
||||
source-map-support: 0.5.19
|
||||
typescript: 4.3.0-dev.20210423
|
||||
typescript: 4.2.4
|
||||
yn: 3.1.1
|
||||
dev: true
|
||||
|
||||
@@ -13324,8 +13324,8 @@ packages:
|
||||
/typedarray/0.0.6:
|
||||
resolution: {integrity: sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=}
|
||||
|
||||
/typescript/4.3.0-dev.20210423:
|
||||
resolution: {integrity: sha512-2rJFJX6594459dFyxWkrl4Io3Gs140NylUSlds/WWzdd/kB4YTeM0iXu5fZLv6XM1GOLSnwnyDmUEu98Z2SEkQ==}
|
||||
/typescript/4.2.4:
|
||||
resolution: {integrity: sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==}
|
||||
engines: {node: '>=4.2.0'}
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
Reference in New Issue
Block a user