mirror of
https://github.com/pnpm/pnpm.git
synced 2026-02-02 19:22:52 -05:00
9
.changeset/cyan-houses-call.md
Normal file
9
.changeset/cyan-houses-call.md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
"@pnpm/core": minor
|
||||
"@pnpm/hooks.read-package-hook": major
|
||||
"@pnpm/outdated": minor
|
||||
"@pnpm/parse-overrides": minor
|
||||
"pnpm": minor
|
||||
---
|
||||
|
||||
Overrides now support catalogs [#8303](https://github.com/pnpm/pnpm/issues/8303).
|
||||
@@ -29,6 +29,8 @@
|
||||
"homepage": "https://github.com/pnpm/pnpm/blob/main/config/parse-overrides#readme",
|
||||
"funding": "https://opencollective.com/pnpm",
|
||||
"dependencies": {
|
||||
"@pnpm/catalogs.resolver": "workspace:*",
|
||||
"@pnpm/catalogs.types": "workspace:*",
|
||||
"@pnpm/error": "workspace:*",
|
||||
"@pnpm/parse-wanted-dependency": "workspace:*"
|
||||
},
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import { parseWantedDependency } from '@pnpm/parse-wanted-dependency'
|
||||
import { matchCatalogResolveResult, resolveFromCatalog } from '@pnpm/catalogs.resolver'
|
||||
import { type Catalogs } from '@pnpm/catalogs.types'
|
||||
|
||||
const DELIMITER_REGEX = /[^ |@]>/
|
||||
|
||||
export interface VersionOverride {
|
||||
selector: string
|
||||
parentPkg?: PackageSelector
|
||||
targetPkg: PackageSelector
|
||||
newPref: string
|
||||
@@ -15,28 +18,47 @@ export interface PackageSelector {
|
||||
}
|
||||
|
||||
export function parseOverrides (
|
||||
overrides: Record<string, string>
|
||||
overrides: Record<string, string>,
|
||||
catalogs?: Catalogs
|
||||
): VersionOverride[] {
|
||||
const _resolveFromCatalog = resolveFromCatalog.bind(null, catalogs ?? {})
|
||||
return Object.entries(overrides)
|
||||
.map(([selector, newPref]) => {
|
||||
let delimiterIndex = selector.search(DELIMITER_REGEX)
|
||||
if (delimiterIndex !== -1) {
|
||||
delimiterIndex++
|
||||
const parentSelector = selector.substring(0, delimiterIndex)
|
||||
const childSelector = selector.substring(delimiterIndex + 1)
|
||||
return {
|
||||
newPref,
|
||||
parentPkg: parsePkgSelector(parentSelector),
|
||||
targetPkg: parsePkgSelector(childSelector),
|
||||
}
|
||||
}
|
||||
const result = parsePkgAndParentSelector(selector)
|
||||
const resolvedCatalog = matchCatalogResolveResult(_resolveFromCatalog({
|
||||
pref: newPref,
|
||||
alias: result.targetPkg.name,
|
||||
}), {
|
||||
found: ({ resolution }) => resolution.specifier,
|
||||
unused: () => undefined,
|
||||
misconfiguration: ({ error }) => {
|
||||
throw new PnpmError('CATALOG_IN_OVERRIDES', `Could not resolve a catalog in the overrides: ${error.message}`)
|
||||
},
|
||||
})
|
||||
return {
|
||||
newPref,
|
||||
targetPkg: parsePkgSelector(selector),
|
||||
selector,
|
||||
newPref: resolvedCatalog ?? newPref,
|
||||
...result,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function parsePkgAndParentSelector (selector: string): Pick<VersionOverride, 'parentPkg' | 'targetPkg'> {
|
||||
let delimiterIndex = selector.search(DELIMITER_REGEX)
|
||||
if (delimiterIndex !== -1) {
|
||||
delimiterIndex++
|
||||
const parentSelector = selector.substring(0, delimiterIndex)
|
||||
const childSelector = selector.substring(delimiterIndex + 1)
|
||||
return {
|
||||
parentPkg: parsePkgSelector(parentSelector),
|
||||
targetPkg: parsePkgSelector(childSelector),
|
||||
}
|
||||
}
|
||||
return {
|
||||
targetPkg: parsePkgSelector(selector),
|
||||
}
|
||||
}
|
||||
|
||||
function parsePkgSelector (selector: string): PackageSelector {
|
||||
const wantedDep = parseWantedDependency(selector)
|
||||
if (!wantedDep.alias) {
|
||||
|
||||
@@ -3,11 +3,11 @@ import { parseOverrides } from '@pnpm/parse-overrides'
|
||||
test.each([
|
||||
[
|
||||
{ foo: '1' },
|
||||
[{ newPref: '1', targetPkg: { name: 'foo' } }],
|
||||
[{ selector: 'foo', newPref: '1', targetPkg: { name: 'foo' } }],
|
||||
],
|
||||
[
|
||||
{ 'foo@2': '1' },
|
||||
[{ newPref: '1', targetPkg: { name: 'foo', pref: '2' } }],
|
||||
[{ selector: 'foo@2', newPref: '1', targetPkg: { name: 'foo', pref: '2' } }],
|
||||
],
|
||||
[
|
||||
{
|
||||
@@ -15,8 +15,8 @@ test.each([
|
||||
'foo@3 || >=2': '1',
|
||||
},
|
||||
[
|
||||
{ newPref: '1', targetPkg: { name: 'foo', pref: '>2' } },
|
||||
{ newPref: '1', targetPkg: { name: 'foo', pref: '3 || >=2' } },
|
||||
{ selector: 'foo@>2', newPref: '1', targetPkg: { name: 'foo', pref: '>2' } },
|
||||
{ selector: 'foo@3 || >=2', newPref: '1', targetPkg: { name: 'foo', pref: '3 || >=2' } },
|
||||
],
|
||||
],
|
||||
[
|
||||
@@ -27,10 +27,10 @@ test.each([
|
||||
'bar@1>foo@1': '2',
|
||||
},
|
||||
[
|
||||
{ newPref: '2', parentPkg: { name: 'bar' }, targetPkg: { name: 'foo' } },
|
||||
{ newPref: '2', parentPkg: { name: 'bar', pref: '1' }, targetPkg: { name: 'foo' } },
|
||||
{ newPref: '2', parentPkg: { name: 'bar' }, targetPkg: { name: 'foo', pref: '1' } },
|
||||
{ newPref: '2', parentPkg: { name: 'bar', pref: '1' }, targetPkg: { name: 'foo', pref: '1' } },
|
||||
{ selector: 'bar>foo', newPref: '2', parentPkg: { name: 'bar' }, targetPkg: { name: 'foo' } },
|
||||
{ selector: 'bar@1>foo', newPref: '2', parentPkg: { name: 'bar', pref: '1' }, targetPkg: { name: 'foo' } },
|
||||
{ selector: 'bar>foo@1', newPref: '2', parentPkg: { name: 'bar' }, targetPkg: { name: 'foo', pref: '1' } },
|
||||
{ selector: 'bar@1>foo@1', newPref: '2', parentPkg: { name: 'bar', pref: '1' }, targetPkg: { name: 'foo', pref: '1' } },
|
||||
],
|
||||
],
|
||||
[
|
||||
@@ -39,14 +39,15 @@ test.each([
|
||||
'foo@3 || >=2>bar@3 || >=2': '1',
|
||||
},
|
||||
[
|
||||
{ newPref: '1', parentPkg: { name: 'foo', pref: '>2' }, targetPkg: { name: 'bar', pref: '>2' } },
|
||||
{ newPref: '1', parentPkg: { name: 'foo', pref: '3 || >=2' }, targetPkg: { name: 'bar', pref: '3 || >=2' } },
|
||||
{ selector: 'foo@>2>bar@>2', newPref: '1', parentPkg: { name: 'foo', pref: '>2' }, targetPkg: { name: 'bar', pref: '>2' } },
|
||||
{ selector: 'foo@3 || >=2>bar@3 || >=2', newPref: '1', parentPkg: { name: 'foo', pref: '3 || >=2' }, targetPkg: { name: 'bar', pref: '3 || >=2' } },
|
||||
],
|
||||
],
|
||||
])('parseOverrides()', (overrides, expectedResult) => {
|
||||
expect(parseOverrides(overrides)).toEqual(expectedResult)
|
||||
expect(parseOverrides(overrides, {})).toEqual(expectedResult)
|
||||
})
|
||||
|
||||
test('parseOverrides() throws an exception on invalid selector', () => {
|
||||
expect(() => parseOverrides({ '%': '2' })).toThrow('Cannot parse the "%" selector')
|
||||
expect(() => parseOverrides({ '%': '2' }, {})).toThrow('Cannot parse the "%" selector')
|
||||
expect(() => parseOverrides({ 'foo > bar': '2' }, {})).toThrow('Cannot parse the "foo > bar" selector')
|
||||
})
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
"../../__typings__/**/*.d.ts"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../catalogs/resolver"
|
||||
},
|
||||
{
|
||||
"path": "../../catalogs/types"
|
||||
},
|
||||
{
|
||||
"path": "../../packages/error"
|
||||
},
|
||||
|
||||
@@ -9,7 +9,7 @@ import isEmpty from 'ramda/src/isEmpty'
|
||||
import pipeWith from 'ramda/src/pipeWith'
|
||||
import { createOptionalDependenciesRemover } from './createOptionalDependenciesRemover'
|
||||
import { createPackageExtender } from './createPackageExtender'
|
||||
import { createVersionsOverrider } from './createVersionsOverrider'
|
||||
import { createVersionsOverrider, type VersionOverrideWithoutRawSelector } from './createVersionsOverrider'
|
||||
|
||||
export function createReadPackageHook (
|
||||
{
|
||||
@@ -22,7 +22,7 @@ export function createReadPackageHook (
|
||||
}: {
|
||||
ignoreCompatibilityDb?: boolean
|
||||
lockfileDir: string
|
||||
overrides?: Record<string, string>
|
||||
overrides?: VersionOverrideWithoutRawSelector[]
|
||||
ignoredOptionalDependencies?: string[]
|
||||
packageExtensions?: Record<string, PackageExtension>
|
||||
readPackageHook?: ReadPackageHook[] | ReadPackageHook
|
||||
|
||||
@@ -2,18 +2,18 @@ import path from 'path'
|
||||
import semver from 'semver'
|
||||
import partition from 'ramda/src/partition'
|
||||
import { type Dependencies, type PackageManifest, type ReadPackageHook } from '@pnpm/types'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import { parseOverrides, type VersionOverride as VersionOverrideBase } from '@pnpm/parse-overrides'
|
||||
import { type VersionOverride as VersionOverrideBase } from '@pnpm/parse-overrides'
|
||||
import normalizePath from 'normalize-path'
|
||||
import { isIntersectingRange } from './isIntersectingRange'
|
||||
|
||||
export type VersionOverrideWithoutRawSelector = Omit<VersionOverrideBase, 'selector'>
|
||||
|
||||
export function createVersionsOverrider (
|
||||
overrides: Record<string, string>,
|
||||
overrides: VersionOverrideWithoutRawSelector[],
|
||||
rootDir: string
|
||||
): ReadPackageHook {
|
||||
const parsedOverrides = tryParseOverrides(overrides)
|
||||
const [versionOverrides, genericVersionOverrides] = partition(({ parentPkg }) => parentPkg != null,
|
||||
parsedOverrides
|
||||
overrides
|
||||
.map((override) => {
|
||||
return {
|
||||
...override,
|
||||
@@ -34,14 +34,6 @@ export function createVersionsOverrider (
|
||||
}) as ReadPackageHook
|
||||
}
|
||||
|
||||
function tryParseOverrides (overrides: Record<string, string>): VersionOverrideBase[] {
|
||||
try {
|
||||
return parseOverrides(overrides)
|
||||
} catch (e) {
|
||||
throw new PnpmError('INVALID_OVERRIDES_SELECTOR', `${(e as PnpmError).message} in pnpm.overrides`)
|
||||
}
|
||||
}
|
||||
|
||||
interface LocalTarget {
|
||||
protocol: LocalProtocol
|
||||
absolutePath: string
|
||||
@@ -50,7 +42,7 @@ interface LocalTarget {
|
||||
|
||||
type LocalProtocol = 'link:' | 'file:'
|
||||
|
||||
function createLocalTarget (override: VersionOverrideBase, rootDir: string): LocalTarget | undefined {
|
||||
function createLocalTarget (override: VersionOverrideWithoutRawSelector, rootDir: string): LocalTarget | undefined {
|
||||
let protocol: LocalProtocol | undefined
|
||||
if (override.newPref.startsWith('file:')) {
|
||||
protocol = 'file:'
|
||||
@@ -69,12 +61,7 @@ interface VersionOverride extends VersionOverrideBase {
|
||||
localTarget?: LocalTarget
|
||||
}
|
||||
|
||||
interface VersionOverrideWithParent extends VersionOverride {
|
||||
parentPkg: {
|
||||
name: string
|
||||
pref?: string
|
||||
}
|
||||
}
|
||||
type VersionOverrideWithParent = VersionOverride & Required<Pick<VersionOverride, 'parentPkg'>>
|
||||
|
||||
function overrideDepsOfPkg (
|
||||
{ manifest, dir }: { manifest: PackageManifest, dir: string | undefined },
|
||||
|
||||
@@ -27,9 +27,14 @@ test('createReadPackageHook() runs the custom hook before the version overrider'
|
||||
ignoreCompatibilityDb: true,
|
||||
lockfileDir: '/foo',
|
||||
readPackageHook: [hook],
|
||||
overrides: {
|
||||
react: '16',
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
targetPkg: {
|
||||
name: 'react',
|
||||
},
|
||||
newPref: '16',
|
||||
},
|
||||
],
|
||||
})
|
||||
const manifest = {}
|
||||
const dir = '/bar'
|
||||
|
||||
@@ -2,10 +2,22 @@ import path from 'path'
|
||||
import { createVersionsOverrider } from '../lib/createVersionsOverrider'
|
||||
|
||||
test('createVersionsOverrider() matches sub-ranges', () => {
|
||||
const overrider = createVersionsOverrider({
|
||||
'foo@2': '2.12.0',
|
||||
'qar@>2': '1.0.0',
|
||||
}, process.cwd())
|
||||
const overrider = createVersionsOverrider([
|
||||
{
|
||||
targetPkg: {
|
||||
name: 'foo',
|
||||
pref: '2',
|
||||
},
|
||||
newPref: '2.12.0',
|
||||
},
|
||||
{
|
||||
targetPkg: {
|
||||
name: 'qar',
|
||||
pref: '>2',
|
||||
},
|
||||
newPref: '1.0.0',
|
||||
},
|
||||
], process.cwd())
|
||||
expect(
|
||||
overrider({
|
||||
dependencies: { foo: '^2.10.0' },
|
||||
@@ -18,10 +30,22 @@ test('createVersionsOverrider() matches sub-ranges', () => {
|
||||
})
|
||||
|
||||
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())
|
||||
const overrider = createVersionsOverrider([
|
||||
{
|
||||
targetPkg: {
|
||||
name: 'foo',
|
||||
pref: '2',
|
||||
},
|
||||
newPref: '2.12.0',
|
||||
},
|
||||
{
|
||||
targetPkg: {
|
||||
name: 'bar',
|
||||
pref: 'github:org/bar',
|
||||
},
|
||||
newPref: '2.12.0',
|
||||
},
|
||||
], process.cwd())
|
||||
expect(
|
||||
overrider({
|
||||
dependencies: {
|
||||
@@ -38,10 +62,30 @@ 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',
|
||||
'qar@1>bar@>4': '3.0.0',
|
||||
}, process.cwd())
|
||||
const overrider = createVersionsOverrider([
|
||||
{
|
||||
parentPkg: {
|
||||
name: 'foo',
|
||||
pref: '1',
|
||||
},
|
||||
targetPkg: {
|
||||
name: 'bar',
|
||||
pref: '^1.2.0',
|
||||
},
|
||||
newPref: '3.0.0',
|
||||
},
|
||||
{
|
||||
parentPkg: {
|
||||
name: 'qar',
|
||||
pref: '1',
|
||||
},
|
||||
targetPkg: {
|
||||
name: 'bar',
|
||||
pref: '>4',
|
||||
},
|
||||
newPref: '3.0.0',
|
||||
},
|
||||
], process.cwd())
|
||||
expect(overrider({
|
||||
name: 'foo',
|
||||
version: '1.2.0',
|
||||
@@ -97,11 +141,26 @@ test('createVersionsOverrider() overrides dependencies of specified packages onl
|
||||
})
|
||||
|
||||
test('createVersionsOverrider() overrides all types of dependencies', () => {
|
||||
const overrider = createVersionsOverrider({
|
||||
foo: '3.0.0',
|
||||
bar: '3.0.0',
|
||||
qar: '3.0.0',
|
||||
}, process.cwd())
|
||||
const overrider = createVersionsOverrider([
|
||||
{
|
||||
targetPkg: {
|
||||
name: 'foo',
|
||||
},
|
||||
newPref: '3.0.0',
|
||||
},
|
||||
{
|
||||
targetPkg: {
|
||||
name: 'bar',
|
||||
},
|
||||
newPref: '3.0.0',
|
||||
},
|
||||
{
|
||||
targetPkg: {
|
||||
name: 'qar',
|
||||
},
|
||||
newPref: '3.0.0',
|
||||
},
|
||||
], process.cwd())
|
||||
expect(overrider({
|
||||
name: 'foo',
|
||||
version: '1.2.0',
|
||||
@@ -130,9 +189,14 @@ test('createVersionsOverrider() overrides all types of dependencies', () => {
|
||||
})
|
||||
|
||||
test('createVersionsOverrider() overrides dependencies with links', () => {
|
||||
const overrider = createVersionsOverrider({
|
||||
qar: 'link:../qar',
|
||||
}, process.cwd())
|
||||
const overrider = createVersionsOverrider([
|
||||
{
|
||||
targetPkg: {
|
||||
name: 'qar',
|
||||
},
|
||||
newPref: 'link:../qar',
|
||||
},
|
||||
], process.cwd())
|
||||
expect(overrider({
|
||||
name: 'foo',
|
||||
version: '1.2.0',
|
||||
@@ -150,9 +214,14 @@ test('createVersionsOverrider() overrides dependencies with links', () => {
|
||||
|
||||
test('createVersionsOverrider() overrides dependencies with absolute links', () => {
|
||||
const qarAbsolutePath = path.resolve(process.cwd(), './qar')
|
||||
const overrider = createVersionsOverrider({
|
||||
qar: `link:${qarAbsolutePath}`,
|
||||
}, process.cwd())
|
||||
const overrider = createVersionsOverrider([
|
||||
{
|
||||
targetPkg: {
|
||||
name: 'qar',
|
||||
},
|
||||
newPref: `link:${qarAbsolutePath}`,
|
||||
},
|
||||
], process.cwd())
|
||||
|
||||
expect(overrider({
|
||||
name: 'foo',
|
||||
@@ -170,9 +239,18 @@ test('createVersionsOverrider() overrides dependencies with absolute links', ()
|
||||
})
|
||||
|
||||
test('createVersionsOverrider() overrides dependency of pkg matched by name and version', () => {
|
||||
const overrider = createVersionsOverrider({
|
||||
'yargs@^7.1.0>yargs-parser': '^20.0.0',
|
||||
}, process.cwd())
|
||||
const overrider = createVersionsOverrider([
|
||||
{
|
||||
parentPkg: {
|
||||
name: 'yargs',
|
||||
pref: '^7.1.0',
|
||||
},
|
||||
targetPkg: {
|
||||
name: 'yargs-parser',
|
||||
},
|
||||
newPref: '^20.0.0',
|
||||
},
|
||||
], process.cwd())
|
||||
expect(
|
||||
overrider({
|
||||
name: 'yargs',
|
||||
@@ -191,9 +269,18 @@ test('createVersionsOverrider() overrides dependency of pkg matched by name and
|
||||
})
|
||||
|
||||
test('createVersionsOverrider() does not override dependency of pkg matched by name and version', () => {
|
||||
const overrider = createVersionsOverrider({
|
||||
'yargs@^8.1.0>yargs-parser': '^20.0.0',
|
||||
}, process.cwd())
|
||||
const overrider = createVersionsOverrider([
|
||||
{
|
||||
parentPkg: {
|
||||
name: 'yargs',
|
||||
pref: '^8.1.0',
|
||||
},
|
||||
targetPkg: {
|
||||
name: 'yargs-parser',
|
||||
},
|
||||
newPref: '^20.0.0',
|
||||
},
|
||||
], process.cwd())
|
||||
expect(
|
||||
overrider({
|
||||
name: 'yargs',
|
||||
@@ -212,9 +299,17 @@ test('createVersionsOverrider() does not override dependency of pkg matched by n
|
||||
})
|
||||
|
||||
test('createVersionsOverrider() should work for scoped parent and unscoped child', () => {
|
||||
const overrider = createVersionsOverrider({
|
||||
'@scoped/package>unscoped-package': 'workspace:*',
|
||||
}, process.cwd())
|
||||
const overrider = createVersionsOverrider([
|
||||
{
|
||||
parentPkg: {
|
||||
name: '@scoped/package',
|
||||
},
|
||||
targetPkg: {
|
||||
name: 'unscoped-package',
|
||||
},
|
||||
newPref: 'workspace:*',
|
||||
},
|
||||
], process.cwd())
|
||||
expect(
|
||||
overrider({
|
||||
name: '@scoped/package',
|
||||
@@ -233,9 +328,17 @@ test('createVersionsOverrider() should work for scoped parent and unscoped child
|
||||
})
|
||||
|
||||
test('createVersionsOverrider() should work for unscoped parent and scoped child', () => {
|
||||
const overrider = createVersionsOverrider({
|
||||
'unscoped-package>@scoped/package': 'workspace:*',
|
||||
}, process.cwd())
|
||||
const overrider = createVersionsOverrider([
|
||||
{
|
||||
parentPkg: {
|
||||
name: 'unscoped-package',
|
||||
},
|
||||
targetPkg: {
|
||||
name: '@scoped/package',
|
||||
},
|
||||
newPref: 'workspace:*',
|
||||
},
|
||||
], process.cwd())
|
||||
expect(
|
||||
overrider({
|
||||
name: 'unscoped-package',
|
||||
@@ -254,9 +357,17 @@ test('createVersionsOverrider() should work for unscoped parent and scoped child
|
||||
})
|
||||
|
||||
test('createVersionsOverrider() should work for scoped parent and scoped child', () => {
|
||||
const overrider = createVersionsOverrider({
|
||||
'@scoped/package>@scoped/package2': 'workspace:*',
|
||||
}, process.cwd())
|
||||
const overrider = createVersionsOverrider([
|
||||
{
|
||||
parentPkg: {
|
||||
name: '@scoped/package',
|
||||
},
|
||||
targetPkg: {
|
||||
name: '@scoped/package2',
|
||||
},
|
||||
newPref: 'workspace:*',
|
||||
},
|
||||
], process.cwd())
|
||||
expect(
|
||||
overrider({
|
||||
name: '@scoped/package',
|
||||
@@ -276,9 +387,14 @@ test('createVersionsOverrider() should work for scoped parent and scoped child',
|
||||
|
||||
test('createVersionsOverrider() overrides dependencies with file with relative path for root package', () => {
|
||||
const rootDir = process.cwd()
|
||||
const overrider = createVersionsOverrider({
|
||||
qar: 'file:../qar',
|
||||
}, rootDir)
|
||||
const overrider = createVersionsOverrider([
|
||||
{
|
||||
targetPkg: {
|
||||
name: 'qar',
|
||||
},
|
||||
newPref: 'file:../qar',
|
||||
},
|
||||
], rootDir)
|
||||
expect(overrider({
|
||||
name: 'foo',
|
||||
version: '1.2.0',
|
||||
@@ -296,9 +412,14 @@ test('createVersionsOverrider() overrides dependencies with file with relative p
|
||||
|
||||
test('createVersionsOverrider() overrides dependencies with file with relative path for workspace package', () => {
|
||||
const rootDir = process.cwd()
|
||||
const overrider = createVersionsOverrider({
|
||||
qar: 'file:../qar',
|
||||
}, rootDir)
|
||||
const overrider = createVersionsOverrider([
|
||||
{
|
||||
targetPkg: {
|
||||
name: 'qar',
|
||||
},
|
||||
newPref: 'file:../qar',
|
||||
},
|
||||
], rootDir)
|
||||
expect(overrider({
|
||||
name: 'foo',
|
||||
version: '1.2.0',
|
||||
@@ -316,9 +437,14 @@ test('createVersionsOverrider() overrides dependencies with file with relative p
|
||||
|
||||
test('createVersionsOverrider() overrides dependencies with file specified with absolute path', () => {
|
||||
const absolutePath = path.join(__dirname, 'qar')
|
||||
const overrider = createVersionsOverrider({
|
||||
qar: `file:${absolutePath}`,
|
||||
}, process.cwd())
|
||||
const overrider = createVersionsOverrider([
|
||||
{
|
||||
targetPkg: {
|
||||
name: 'qar',
|
||||
},
|
||||
newPref: `file:${absolutePath}`,
|
||||
},
|
||||
], process.cwd())
|
||||
expect(overrider({
|
||||
name: 'foo',
|
||||
version: '1.2.0',
|
||||
@@ -335,13 +461,48 @@ test('createVersionsOverrider() overrides dependencies with file specified with
|
||||
})
|
||||
|
||||
test('createVersionOverride() should use the most specific rule when both override rules match the same target', () => {
|
||||
const overrider = createVersionsOverrider({
|
||||
foo: '3.0.0',
|
||||
'foo@3': '4.0.0',
|
||||
'foo@2': '2.12.0',
|
||||
'bar>foo@2': 'github:org/foo',
|
||||
'bar>foo@3': '5.0.0',
|
||||
}, process.cwd())
|
||||
const overrider = createVersionsOverrider([
|
||||
{
|
||||
targetPkg: {
|
||||
name: 'foo',
|
||||
},
|
||||
newPref: '3.0.0',
|
||||
},
|
||||
{
|
||||
targetPkg: {
|
||||
name: 'foo',
|
||||
pref: '3',
|
||||
},
|
||||
newPref: '4.0.0',
|
||||
},
|
||||
{
|
||||
targetPkg: {
|
||||
name: 'foo',
|
||||
pref: '2',
|
||||
},
|
||||
newPref: '2.12.0',
|
||||
},
|
||||
{
|
||||
parentPkg: {
|
||||
name: 'bar',
|
||||
},
|
||||
targetPkg: {
|
||||
name: 'foo',
|
||||
pref: '2',
|
||||
},
|
||||
newPref: 'github:org/foo',
|
||||
},
|
||||
{
|
||||
parentPkg: {
|
||||
name: 'bar',
|
||||
},
|
||||
targetPkg: {
|
||||
name: 'foo',
|
||||
pref: '3',
|
||||
},
|
||||
newPref: '5.0.0',
|
||||
},
|
||||
], process.cwd())
|
||||
expect(
|
||||
overrider({
|
||||
dependencies: {
|
||||
@@ -407,16 +568,16 @@ test('createVersionOverride() should use the most specific rule when both overri
|
||||
})
|
||||
})
|
||||
|
||||
test('createVersionOverrider() throws error when supplied an invalid selector', () => {
|
||||
expect(() => createVersionsOverrider({
|
||||
'foo > bar': '2',
|
||||
}, process.cwd())).toThrowError('Cannot parse the "foo > bar" selector in pnpm.overrides')
|
||||
})
|
||||
|
||||
test('createVersionsOverrider() matches intersections', () => {
|
||||
const overrider = createVersionsOverrider({
|
||||
'foo@<1.2.4': '>=1.2.4',
|
||||
}, process.cwd())
|
||||
const overrider = createVersionsOverrider([
|
||||
{
|
||||
targetPkg: {
|
||||
name: 'foo',
|
||||
pref: '<1.2.4',
|
||||
},
|
||||
newPref: '>=1.2.4',
|
||||
},
|
||||
], process.cwd())
|
||||
expect(
|
||||
overrider({
|
||||
dependencies: { foo: '^1.2.3' },
|
||||
@@ -427,9 +588,17 @@ test('createVersionsOverrider() matches intersections', () => {
|
||||
})
|
||||
|
||||
test('createVersionsOverrider() overrides peerDependencies of another dependency', () => {
|
||||
const overrider = createVersionsOverrider({
|
||||
'react-dom>react': '18.1.0',
|
||||
}, process.cwd())
|
||||
const overrider = createVersionsOverrider([
|
||||
{
|
||||
parentPkg: {
|
||||
name: 'react-dom',
|
||||
},
|
||||
targetPkg: {
|
||||
name: 'react',
|
||||
},
|
||||
newPref: '18.1.0',
|
||||
},
|
||||
], process.cwd())
|
||||
expect(
|
||||
overrider({
|
||||
name: 'react-dom',
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"@pnpm/normalize-registries": "workspace:*",
|
||||
"@pnpm/npm-package-arg": "catalog:",
|
||||
"@pnpm/package-requester": "workspace:*",
|
||||
"@pnpm/parse-overrides": "workspace:*",
|
||||
"@pnpm/parse-wanted-dependency": "workspace:*",
|
||||
"@pnpm/pkg-manager.direct-dep-linker": "workspace:*",
|
||||
"@pnpm/prune-lockfile": "workspace:*",
|
||||
|
||||
@@ -3,8 +3,9 @@ import { resolveDependencies, getWantedDependencies } from '@pnpm/resolve-depend
|
||||
import { type PeerDependencyIssuesByProjects } from '@pnpm/types'
|
||||
import { getContext, type GetContextOptions, type ProjectOptions } from '@pnpm/get-context'
|
||||
import { createReadPackageHook } from '@pnpm/hooks.read-package-hook'
|
||||
import { type InstallOptions } from './install/extendInstallOptions'
|
||||
import { DEFAULT_REGISTRIES } from '@pnpm/normalize-registries'
|
||||
import { parseOverrides } from '@pnpm/parse-overrides'
|
||||
import { type InstallOptions } from './install/extendInstallOptions'
|
||||
|
||||
export type ListMissingPeersOptions = Partial<GetContextOptions>
|
||||
& Pick<InstallOptions, 'hooks'
|
||||
@@ -51,6 +52,7 @@ export async function getPeerDependencyIssues (
|
||||
ctx.wantedLockfile.packages,
|
||||
Object.values(ctx.projects).map(({ manifest }) => manifest)
|
||||
)
|
||||
const overrides = parseOverrides(opts.overrides ?? {}, opts.catalogs ?? {})
|
||||
const {
|
||||
peerDependencyIssuesByProjects,
|
||||
waitTillAllFetchingsFinish,
|
||||
@@ -71,7 +73,7 @@ export async function getPeerDependencyIssues (
|
||||
readPackage: createReadPackageHook({
|
||||
ignoreCompatibilityDb: opts.ignoreCompatibilityDb,
|
||||
lockfileDir,
|
||||
overrides: opts.overrides,
|
||||
overrides,
|
||||
packageExtensions: opts.packageExtensions,
|
||||
readPackageHook: opts.hooks?.readPackage,
|
||||
ignoredOptionalDependencies: opts.ignoredOptionalDependencies,
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
type Registries,
|
||||
type PrepareExecutionEnv,
|
||||
} from '@pnpm/types'
|
||||
import { parseOverrides, type VersionOverride } from '@pnpm/parse-overrides'
|
||||
import { pnpmPkgJson } from '../pnpmPkgJson'
|
||||
import { type ReporterFunction } from '../types'
|
||||
import { type PreResolutionHookContext } from '@pnpm/hooks.types'
|
||||
@@ -252,6 +253,7 @@ const defaults = (opts: InstallOptions): StrictInstallOptions => {
|
||||
|
||||
export type ProcessedInstallOptions = StrictInstallOptions & {
|
||||
readPackageHook?: ReadPackageHook
|
||||
parsedOverrides: VersionOverride[]
|
||||
}
|
||||
|
||||
export function extendOptions (
|
||||
@@ -272,11 +274,12 @@ export function extendOptions (
|
||||
...defaultOpts,
|
||||
...opts,
|
||||
storeDir: defaultOpts.storeDir,
|
||||
parsedOverrides: parseOverrides(opts.overrides ?? {}, opts.catalogs ?? {}),
|
||||
}
|
||||
extendedOpts.readPackageHook = createReadPackageHook({
|
||||
ignoreCompatibilityDb: extendedOpts.ignoreCompatibilityDb,
|
||||
readPackageHook: extendedOpts.hooks?.readPackage,
|
||||
overrides: extendedOpts.overrides,
|
||||
overrides: extendedOpts.parsedOverrides,
|
||||
lockfileDir: extendedOpts.lockfileDir,
|
||||
packageExtensions: extendedOpts.packageExtensions,
|
||||
ignoredOptionalDependencies: extendedOpts.ignoredOptionalDependencies,
|
||||
|
||||
@@ -340,12 +340,13 @@ export async function mutateModules (
|
||||
const frozenLockfile = opts.frozenLockfile ||
|
||||
opts.frozenLockfileIfExists && ctx.existsNonEmptyWantedLockfile
|
||||
let outdatedLockfileSettings = false
|
||||
const overridesMap = Object.fromEntries((opts.parsedOverrides ?? []).map(({ selector, newPref }) => [selector, newPref]))
|
||||
if (!opts.ignorePackageManifest) {
|
||||
const outdatedLockfileSettingName = getOutdatedLockfileSetting(ctx.wantedLockfile, {
|
||||
autoInstallPeers: opts.autoInstallPeers,
|
||||
excludeLinksFromLockfile: opts.excludeLinksFromLockfile,
|
||||
peersSuffixMaxLength: opts.peersSuffixMaxLength,
|
||||
overrides: opts.overrides,
|
||||
overrides: overridesMap,
|
||||
ignoredOptionalDependencies: opts.ignoredOptionalDependencies?.sort(),
|
||||
packageExtensionsChecksum,
|
||||
patchedDependencies,
|
||||
@@ -367,7 +368,7 @@ export async function mutateModules (
|
||||
excludeLinksFromLockfile: opts.excludeLinksFromLockfile,
|
||||
peersSuffixMaxLength: opts.peersSuffixMaxLength,
|
||||
}
|
||||
ctx.wantedLockfile.overrides = opts.overrides
|
||||
ctx.wantedLockfile.overrides = overridesMap
|
||||
ctx.wantedLockfile.packageExtensionsChecksum = packageExtensionsChecksum
|
||||
ctx.wantedLockfile.ignoredOptionalDependencies = opts.ignoredOptionalDependencies
|
||||
ctx.wantedLockfile.pnpmfileChecksum = pnpmfileChecksum
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createPeersDirSuffix } from '@pnpm/dependency-path'
|
||||
import { type ProjectRootDir, type ProjectId, type ProjectManifest } from '@pnpm/types'
|
||||
import { prepareEmpty } from '@pnpm/prepare'
|
||||
import { addDistTag } from '@pnpm/registry-mock'
|
||||
import { type MutatedProject, mutateModules, type ProjectOptions, type MutateModulesOptions, addDependenciesToPackage } from '@pnpm/core'
|
||||
import path from 'path'
|
||||
import { testDefaults } from './utils'
|
||||
@@ -678,3 +679,71 @@ describe('update', () => {
|
||||
expect(Object.keys(readLockfile().snapshots)).toEqual(['is-positive@1.0.0'])
|
||||
})
|
||||
})
|
||||
|
||||
test('catalogs work in overrides', async () => {
|
||||
await addDistTag({ package: '@pnpm.e2e/bar', version: '100.0.0', distTag: 'latest' })
|
||||
await addDistTag({ package: '@pnpm.e2e/foo', version: '100.0.0', distTag: 'latest' })
|
||||
|
||||
const overrides: Record<string, string> = {
|
||||
'@pnpm.e2e/foobarqar>@pnpm.e2e/foo': 'catalog:',
|
||||
'@pnpm.e2e/bar@^100.0.0': 'catalog:',
|
||||
'@pnpm.e2e/dep-of-pkg-with-1-dep': 'catalog:',
|
||||
}
|
||||
|
||||
const { options, projects, readLockfile } = preparePackagesAndReturnObjects([
|
||||
{
|
||||
name: 'project1',
|
||||
dependencies: {
|
||||
'@pnpm.e2e/pkg-with-1-dep': '100.0.0',
|
||||
'@pnpm.e2e/foobar': '100.0.0',
|
||||
'@pnpm.e2e/foobarqar': '1.0.0',
|
||||
},
|
||||
},
|
||||
// Empty second project to create a multi-package workspace.
|
||||
{
|
||||
name: 'project2',
|
||||
},
|
||||
])
|
||||
|
||||
const catalogs = {
|
||||
default: {
|
||||
'@pnpm.e2e/foo': 'npm:@pnpm.e2e/qar@100.0.0',
|
||||
'@pnpm.e2e/bar': '100.1.0',
|
||||
'@pnpm.e2e/dep-of-pkg-with-1-dep': '101.0.0',
|
||||
},
|
||||
}
|
||||
await mutateModules(installProjects(projects), {
|
||||
...options,
|
||||
lockfileOnly: true,
|
||||
catalogs,
|
||||
overrides,
|
||||
})
|
||||
|
||||
let lockfile = readLockfile()
|
||||
expect(lockfile.snapshots['@pnpm.e2e/foobarqar@1.0.0'].dependencies?.['@pnpm.e2e/foo']).toBe('@pnpm.e2e/qar@100.0.0')
|
||||
expect(lockfile.snapshots['@pnpm.e2e/foobar@100.0.0'].dependencies?.['@pnpm.e2e/foo']).toBe('100.0.0')
|
||||
expect(lockfile.packages).toHaveProperty(['@pnpm.e2e/dep-of-pkg-with-1-dep@101.0.0'])
|
||||
expect(lockfile.packages).toHaveProperty(['@pnpm.e2e/bar@100.1.0'])
|
||||
expect(lockfile.overrides).toStrictEqual({
|
||||
'@pnpm.e2e/foobarqar>@pnpm.e2e/foo': 'npm:@pnpm.e2e/qar@100.0.0',
|
||||
'@pnpm.e2e/bar@^100.0.0': '100.1.0',
|
||||
'@pnpm.e2e/dep-of-pkg-with-1-dep': '101.0.0',
|
||||
})
|
||||
|
||||
catalogs.default['@pnpm.e2e/bar'] = '100.0.0'
|
||||
await mutateModules(installProjects(projects), {
|
||||
...options,
|
||||
lockfileOnly: true,
|
||||
catalogs,
|
||||
overrides,
|
||||
})
|
||||
|
||||
lockfile = readLockfile()
|
||||
expect(lockfile.packages).toHaveProperty(['@pnpm.e2e/bar@100.0.0'])
|
||||
expect(lockfile.packages).not.toHaveProperty(['@pnpm.e2e/bar@100.1.0'])
|
||||
expect(lockfile.overrides).toStrictEqual({
|
||||
'@pnpm.e2e/foobarqar>@pnpm.e2e/foo': 'npm:@pnpm.e2e/qar@100.0.0',
|
||||
'@pnpm.e2e/bar@^100.0.0': '100.0.0',
|
||||
'@pnpm.e2e/dep-of-pkg-with-1-dep': '101.0.0',
|
||||
})
|
||||
})
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
{
|
||||
"path": "../../config/normalize-registries"
|
||||
},
|
||||
{
|
||||
"path": "../../config/parse-overrides"
|
||||
},
|
||||
{
|
||||
"path": "../../crypto/object-hasher"
|
||||
},
|
||||
|
||||
12
pnpm-lock.yaml
generated
12
pnpm-lock.yaml
generated
@@ -1432,6 +1432,12 @@ importers:
|
||||
|
||||
config/parse-overrides:
|
||||
dependencies:
|
||||
'@pnpm/catalogs.resolver':
|
||||
specifier: workspace:*
|
||||
version: link:../../catalogs/resolver
|
||||
'@pnpm/catalogs.types':
|
||||
specifier: workspace:*
|
||||
version: link:../../catalogs/types
|
||||
'@pnpm/error':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/error
|
||||
@@ -3848,6 +3854,9 @@ importers:
|
||||
'@pnpm/package-requester':
|
||||
specifier: workspace:*
|
||||
version: link:../package-requester
|
||||
'@pnpm/parse-overrides':
|
||||
specifier: workspace:*
|
||||
version: link:../../config/parse-overrides
|
||||
'@pnpm/parse-wanted-dependency':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/parse-wanted-dependency
|
||||
@@ -6270,6 +6279,9 @@ importers:
|
||||
'@pnpm/npm-resolver':
|
||||
specifier: workspace:*
|
||||
version: link:../../resolving/npm-resolver
|
||||
'@pnpm/parse-overrides':
|
||||
specifier: workspace:*
|
||||
version: link:../../config/parse-overrides
|
||||
'@pnpm/pick-registry-for-package':
|
||||
specifier: workspace:*
|
||||
version: link:../../config/pick-registry-for-package
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
"@pnpm/matcher": "workspace:*",
|
||||
"@pnpm/modules-yaml": "workspace:*",
|
||||
"@pnpm/npm-resolver": "workspace:*",
|
||||
"@pnpm/parse-overrides": "workspace:*",
|
||||
"@pnpm/pick-registry-for-package": "workspace:*",
|
||||
"@pnpm/types": "workspace:*",
|
||||
"ramda": "catalog:",
|
||||
|
||||
@@ -28,6 +28,7 @@ import * as dp from '@pnpm/dependency-path'
|
||||
import semver from 'semver'
|
||||
import { createMatcher } from '@pnpm/matcher'
|
||||
import { createReadPackageHook } from '@pnpm/hooks.read-package-hook'
|
||||
import { parseOverrides } from '@pnpm/parse-overrides'
|
||||
|
||||
export * from './createManifestGetter'
|
||||
|
||||
@@ -69,7 +70,7 @@ export async function outdated (
|
||||
if (overrides) {
|
||||
const readPackageHook = createReadPackageHook({
|
||||
lockfileDir: opts.lockfileDir,
|
||||
overrides,
|
||||
overrides: parseOverrides(overrides, opts.catalogs ?? {}),
|
||||
})
|
||||
const manifest = await readPackageHook?.(opts.manifest, opts.lockfileDir)
|
||||
if (manifest) return manifest
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
{
|
||||
"path": "../../config/matcher"
|
||||
},
|
||||
{
|
||||
"path": "../../config/parse-overrides"
|
||||
},
|
||||
{
|
||||
"path": "../../config/pick-registry-for-package"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user