diff --git a/.changeset/dollar-overrides-deprecation.md b/.changeset/dollar-overrides-deprecation.md new file mode 100644 index 0000000000..92b8ebd720 --- /dev/null +++ b/.changeset/dollar-overrides-deprecation.md @@ -0,0 +1,6 @@ +--- +"@pnpm/config.reader": patch +"pnpm": patch +--- + +Using the `$` version reference syntax in `overrides` (e.g. `"react": "$react"`) now prints a deprecation warning. The syntax still works, but [catalogs](https://pnpm.io/catalogs) are the recommended way to keep an overridden version in sync with the rest of the workspace. Reference a catalog entry with the `catalog:` protocol instead. diff --git a/config/reader/src/getOptionsFromRootManifest.ts b/config/reader/src/getOptionsFromRootManifest.ts index dd200c86d4..0c7fe8864a 100644 --- a/config/reader/src/getOptionsFromRootManifest.ts +++ b/config/reader/src/getOptionsFromRootManifest.ts @@ -2,6 +2,7 @@ import path from 'node:path' import { envReplace } from '@pnpm/config.env-replace' import { PnpmError } from '@pnpm/error' +import { globalWarn } from '@pnpm/logger' import type { AllowedDeprecatedVersions, PackageExtension, @@ -31,8 +32,11 @@ export function getOptionsFromPnpmSettings (manifestDir: string | undefined, pnp assertValidOverrides(settings.overrides) if (Object.keys(settings.overrides).length === 0) { delete settings.overrides - } else if (manifest) { - settings.overrides = mapValues(createVersionReferencesReplacer(manifest), settings.overrides) + } else { + warnAboutDeprecatedVersionReferences(settings.overrides) + if (manifest) { + settings.overrides = mapValues(createVersionReferencesReplacer(manifest), settings.overrides) + } } } if (pnpmSettings.patchedDependencies) { @@ -92,6 +96,16 @@ function replaceEnvInStringValues (value: unknown): unknown { return out } +function warnAboutDeprecatedVersionReferences (overrides: Record): void { + const selectors = Object.keys(overrides).filter((selector) => overrides[selector][0] === '$') + if (selectors.length === 0) return + globalWarn( + `The "$" version reference syntax in overrides is deprecated (used by: ${selectors.join(', ')}). ` + + 'Define the version in a catalog and reference it with the "catalog:" protocol instead. ' + + 'See https://pnpm.io/catalogs' + ) +} + function createVersionReferencesReplacer (manifest: ProjectManifest): (spec: string) => string { const allDeps = { ...manifest.devDependencies, diff --git a/config/reader/test/deprecatedVersionReferences.test.ts b/config/reader/test/deprecatedVersionReferences.test.ts new file mode 100644 index 0000000000..92b1c759bb --- /dev/null +++ b/config/reader/test/deprecatedVersionReferences.test.ts @@ -0,0 +1,44 @@ +import { beforeEach, expect, jest, test } from '@jest/globals' + +jest.unstable_mockModule('@pnpm/logger', () => ({ + globalWarn: jest.fn(), +})) + +const { globalWarn } = await import('@pnpm/logger') +const { getOptionsFromPnpmSettings } = await import('../lib/getOptionsFromRootManifest.js') + +beforeEach(() => { + jest.mocked(globalWarn).mockClear() +}) + +test('getOptionsFromPnpmSettings() warns about deprecated "$" version references and still resolves them', () => { + const options = getOptionsFromPnpmSettings(process.cwd(), { + overrides: { + foo: '$foo', + }, + }, { + dependencies: { + foo: '^1.0.0', + }, + }) + expect(options.overrides).toStrictEqual({ foo: '^1.0.0' }) + expect(globalWarn).toHaveBeenCalledTimes(1) + const warning = jest.mocked(globalWarn).mock.calls[0][0] + expect(warning).toContain('deprecated') + expect(warning).toContain('foo') + expect(warning).toContain('catalog:') +}) + +test('getOptionsFromPnpmSettings() does not warn when no "$" version references are used', () => { + getOptionsFromPnpmSettings(process.cwd(), { + overrides: { + foo: '^1.0.0', + bar: 'catalog:', + }, + }, { + dependencies: { + foo: '^1.0.0', + }, + }) + expect(globalWarn).not.toHaveBeenCalled() +})