fix: link overrides should work on non-root workspace projects (#3388)

This commit is contained in:
Zoltan Kochan
2021-04-24 12:47:17 +03:00
committed by GitHub
parent 4da26031be
commit 97c64bae4d
11 changed files with 157 additions and 108 deletions

View File

@@ -0,0 +1,5 @@
---
"supi": patch
---
It should be possible to override dependencies with links.

View File

@@ -0,0 +1,5 @@
---
"@pnpm/get-context": minor
---
Pass in the location of the project to the `readPackage` hook.

View 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.

View File

@@ -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"
},

View File

@@ -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)
}
}

View File

@@ -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)
}

View File

@@ -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,

View File

@@ -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',
},
})
})

View File

@@ -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',
},
})
})

View File

@@ -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
View File

@@ -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