From 2d1bf059a239dff3af203104491a315b894b8796 Mon Sep 17 00:00:00 2001 From: Jamie Mason Date: Mon, 30 May 2022 18:48:17 +0100 Subject: [PATCH] feat(pnpm): add support for pnpm overrides Closes #78 BREAKING CHANGE: The `--overrides` option delivered in 6.0.0 was originally intended to support pnpm, but erroneously read from the `.overrides` property of package.json files and not `.pnpm.overrides`. However, npm now also has an `.overrides` property to support the same functionality for users of npm. From this release, the `--overrides` option of syncpack now refers to npm overrides. Pnpm users should change to using the new `--pnpmOverrides` option instead. --- README.md | 28 +++--- src/bin-fix-mismatches/fix-mismatches.spec.ts | 24 +++++ src/bin-fix-mismatches/fix-mismatches.ts | 6 +- src/bin-fix-mismatches/index.ts | 1 + src/bin-lint-semver-ranges/index.ts | 1 + .../lint-semver-ranges.spec.ts | 6 +- src/bin-list-mismatches/index.ts | 1 + src/bin-list/index.ts | 1 + src/bin-list/list.spec.ts | 16 +++- src/bin-set-semver-ranges/index.ts | 1 + .../set-semver-ranges.ts | 7 +- src/constants.ts | 9 +- src/lib/get-input/get-config.ts | 18 +++- src/lib/get-input/get-input.spec.ts | 11 ++- src/lib/get-input/get-instances.ts | 39 +++++--- src/lib/get-input/get-wrappers/index.ts | 5 +- src/option.ts | 3 +- test/mock.ts | 25 ++++- test/scenarios/index.ts | 93 +++++++++++++++++-- 19 files changed, 248 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 80dd5ee5..121732d6 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,8 @@ See [`versionGroups`](#versiongroups) if you have advanced requirements. -d, --dev include devDependencies -P, --peer include peerDependencies -R, --resolutions include resolutions (yarn) --o, --overrides include overrides (pnpm) +-o, --overrides include overrides (npm) +-O, --pnpmOverrides include overrides (pnpm) -w, --workspace include locally developed package versions -i, --indent [value] override indentation. defaults to " " -c, --config path to a syncpack config file @@ -120,7 +121,8 @@ See [`semverGroups`](#semvergroups) if you have advanced requirements. -d, --dev include devDependencies -P, --peer include peerDependencies -R, --resolutions include resolutions (yarn) --o, --overrides include overrides (pnpm) +-o, --overrides include overrides (npm) +-O, --pnpmOverrides include overrides (pnpm) -f, --filter [pattern] only include dependencies whose name matches this regex -r, --semver-range see supported ranges below. defaults to "" -c, --config path to a syncpack config file @@ -165,7 +167,8 @@ List all dependencies required by your packages. -d, --dev include devDependencies -P, --peer include peerDependencies -R, --resolutions include resolutions (yarn) --o, --overrides include overrides (pnpm) +-o, --overrides include overrides (npm) +-O, --pnpmOverrides include overrides (pnpm) -f, --filter [pattern] only include dependencies whose name matches this regex -c, --config path to a syncpack config file -w, --workspace include locally developed package versions @@ -210,7 +213,8 @@ See [`versionGroups`](#versiongroups) if you have advanced requirements. -d, --dev include devDependencies -P, --peer include peerDependencies -R, --resolutions include resolutions (yarn) --o, --overrides include overrides (pnpm) +-o, --overrides include overrides (npm) +-O, --pnpmOverrides include overrides (pnpm) -f, --filter [pattern] only include dependencies whose name matches this regex -c, --config path to a syncpack config file -w, --workspace include locally developed package versions @@ -257,7 +261,8 @@ See [`semverGroups`](#semvergroups) if you have advanced requirements. -d, --dev include devDependencies -P, --peer include peerDependencies -R, --resolutions include resolutions (yarn) --o, --overrides include overrides (pnpm) +-o, --overrides include overrides (npm) +-O, --pnpmOverrides include overrides (pnpm) -w, --workspace include locally developed package versions -i, --indent [value] override indentation. defaults to " " -r, --semver-range see supported ranges below. defaults to "" @@ -315,6 +320,7 @@ configuration file (if present), you can use the `--config` option. "indent": " ", "overrides": true, "peer": true, + "pnpmOverrides": true, "prod": true, "resolutions": true, "workspace": true, @@ -335,17 +341,17 @@ configuration file (if present), you can use the `--config` option. } ``` -### `dev`, `peer`, `prod`, `resolutions`, `overrides`, and `workspace` +### `dev`, `peer`, `prod`, `resolutions`, `overrides`, `pnpmOverrides`, and `workspace` Whether to search within `devDependencies`, `peerDependencies`, `dependencies`, -`resolutions` (Yarn), `overrides` (Pnpm), and the `version` property of the -package.json files of your own packages developed within your `workspace` -respectively. +`resolutions` (Yarn), `overrides` (npm), `pnpmOverrides` (pnpm), and the +`version` property of the package.json files of your own packages developed +within your `workspace` respectively. All of these locations are searched by default but they can be disabled individually in your config file. If any are set via the command line options -`--dev`, `--peer`, `--prod`, `--resolutions`, `--overrides`, or `--workspace` -then only the options you provide will be searched. +`--dev`, `--peer`, `--prod`, `--resolutions`, `--overrides`, `--pnpmOverrides`, +or `--workspace` then only the options you provide will be searched. ### `filter` diff --git a/src/bin-fix-mismatches/fix-mismatches.spec.ts b/src/bin-fix-mismatches/fix-mismatches.spec.ts index bef7da0c..3624be58 100644 --- a/src/bin-fix-mismatches/fix-mismatches.spec.ts +++ b/src/bin-fix-mismatches/fix-mismatches.spec.ts @@ -82,5 +82,29 @@ describe('fixMismatches', () => { scenario.files['packages/b/package.json'].logEntryWhenChanged, ]); }); + + it('replaces mismatching npm overrides', () => { + const scenario = scenarios.dependentDoesNotMatchNpmOverrideVersion(); + fixMismatches(getInput(scenario.disk, scenario.config), scenario.disk); + expect(scenario.disk.writeFileSync.mock.calls).toEqual([ + scenario.files['packages/a/package.json'].diskWriteWhenChanged, + ]); + expect(scenario.log.mock.calls).toEqual([ + scenario.files['packages/a/package.json'].logEntryWhenChanged, + scenario.files['packages/b/package.json'].logEntryWhenUnchanged, + ]); + }); + + it('replaces mismatching pnpm overrides', () => { + const scenario = scenarios.dependentDoesNotMatchPnpmOverrideVersion(); + fixMismatches(getInput(scenario.disk, scenario.config), scenario.disk); + expect(scenario.disk.writeFileSync.mock.calls).toEqual([ + scenario.files['packages/a/package.json'].diskWriteWhenChanged, + ]); + expect(scenario.log.mock.calls).toEqual([ + scenario.files['packages/a/package.json'].logEntryWhenChanged, + scenario.files['packages/b/package.json'].logEntryWhenUnchanged, + ]); + }); }); }); diff --git a/src/bin-fix-mismatches/fix-mismatches.ts b/src/bin-fix-mismatches/fix-mismatches.ts index 27f0e07e..7ddc48d5 100644 --- a/src/bin-fix-mismatches/fix-mismatches.ts +++ b/src/bin-fix-mismatches/fix-mismatches.ts @@ -27,7 +27,11 @@ export function fixMismatches(input: ProgramInput, disk: Disk): void { instances.forEach(({ dependencyType, version, wrapper }) => { const root: any = wrapper.contents; if (version !== nextVersion) { - root[dependencyType][name] = nextVersion; + if (dependencyType === 'pnpmOverrides') { + root.pnpm.overrides[name] = nextVersion; + } else { + root[dependencyType][name] = nextVersion; + } } }); } diff --git a/src/bin-fix-mismatches/index.ts b/src/bin-fix-mismatches/index.ts index f070e248..1526386b 100644 --- a/src/bin-fix-mismatches/index.ts +++ b/src/bin-fix-mismatches/index.ts @@ -71,6 +71,7 @@ fixMismatches( indent: program.opts().indent, overrides: program.opts().overrides, peer: program.opts().peer, + pnpmOverrides: program.opts().pnpmOverrides, prod: program.opts().prod, resolutions: program.opts().resolutions, source: program.opts().source, diff --git a/src/bin-lint-semver-ranges/index.ts b/src/bin-lint-semver-ranges/index.ts index 44862d3c..fa59fcea 100644 --- a/src/bin-lint-semver-ranges/index.ts +++ b/src/bin-lint-semver-ranges/index.ts @@ -79,6 +79,7 @@ lintSemverRanges( filter: program.opts().filter, overrides: program.opts().overrides, peer: program.opts().peer, + pnpmOverrides: program.opts().pnpmOverrides, prod: program.opts().prod, resolutions: program.opts().resolutions, semverRange: program.opts().semverRange, diff --git a/src/bin-lint-semver-ranges/lint-semver-ranges.spec.ts b/src/bin-lint-semver-ranges/lint-semver-ranges.spec.ts index 173d48a4..72c5c58e 100644 --- a/src/bin-lint-semver-ranges/lint-semver-ranges.spec.ts +++ b/src/bin-lint-semver-ranges/lint-semver-ranges.spec.ts @@ -12,8 +12,10 @@ describe('lintSemverRanges', () => { const scenario = scenarios.semverRangesDoNotMatchConfig(); lintSemverRanges(getInput(scenario.disk, scenario.config), scenario.disk); expect(scenario.log.mock.calls).toEqual([ - ['✕ bar 2.0.0 in dependencies of a should be ~2.0.0'], - ['✕ foo 0.1.0 in dependencies of a should be ~0.1.0'], + ['✕ b 0.1.0 in devDependencies of a should be ~0.1.0'], + ['✕ c 0.1.0 in overrides of a should be ~0.1.0'], + ['✕ d 0.1.0 in peerDependencies of a should be ~0.1.0'], + ['✕ e 0.1.0 in resolutions of a should be ~0.1.0'], ]); }); }); diff --git a/src/bin-list-mismatches/index.ts b/src/bin-list-mismatches/index.ts index ee8df423..bae09483 100644 --- a/src/bin-list-mismatches/index.ts +++ b/src/bin-list-mismatches/index.ts @@ -63,6 +63,7 @@ listMismatches( filter: program.opts().filter, overrides: program.opts().overrides, peer: program.opts().peer, + pnpmOverrides: program.opts().pnpmOverrides, prod: program.opts().prod, resolutions: program.opts().resolutions, source: program.opts().source, diff --git a/src/bin-list/index.ts b/src/bin-list/index.ts index 226372f6..7be38642 100644 --- a/src/bin-list/index.ts +++ b/src/bin-list/index.ts @@ -59,6 +59,7 @@ list( filter: program.opts().filter, overrides: program.opts().overrides, peer: program.opts().peer, + pnpmOverrides: program.opts().pnpmOverrides, prod: program.opts().prod, resolutions: program.opts().resolutions, source: program.opts().source, diff --git a/src/bin-list/list.spec.ts b/src/bin-list/list.spec.ts index 55010e37..b24266c3 100644 --- a/src/bin-list/list.spec.ts +++ b/src/bin-list/list.spec.ts @@ -33,7 +33,7 @@ describe('list', () => { }); }); - it('replaces non-semver dependencies with valid semver dependencies', () => { + it('lists non-semver dependencies with valid semver dependencies', () => { const scenario = scenarios.mismatchesIncludeNonSemverVersions(); list(getInput(scenario.disk, scenario.config), scenario.disk); expect(scenario.log.mock.calls).toEqual([ @@ -42,6 +42,20 @@ describe('list', () => { expect(scenario.disk.process.exit).toHaveBeenCalledWith(1); }); + it('lists mismatching npm overrides', () => { + const scenario = scenarios.dependentDoesNotMatchNpmOverrideVersion(); + list(getInput(scenario.disk, scenario.config), scenario.disk); + expect(scenario.log.mock.calls).toEqual([['✕ c 0.1.0, 0.2.0']]); + expect(scenario.disk.process.exit).toHaveBeenCalledWith(1); + }); + + it('lists mismatching pnpm overrides', () => { + const scenario = scenarios.dependentDoesNotMatchPnpmOverrideVersion(); + list(getInput(scenario.disk, scenario.config), scenario.disk); + expect(scenario.log.mock.calls).toEqual([['✕ c 0.1.0, 0.2.0']]); + expect(scenario.disk.process.exit).toHaveBeenCalledWith(1); + }); + it('removes banned/disallowed dependencies', () => { const scenario = scenarios.dependencyIsBanned(); list(getInput(scenario.disk, scenario.config), scenario.disk); diff --git a/src/bin-set-semver-ranges/index.ts b/src/bin-set-semver-ranges/index.ts index 77042f24..ac19c64c 100644 --- a/src/bin-set-semver-ranges/index.ts +++ b/src/bin-set-semver-ranges/index.ts @@ -83,6 +83,7 @@ setSemverRanges( indent: program.opts().indent, overrides: program.opts().overrides, peer: program.opts().peer, + pnpmOverrides: program.opts().pnpmOverrides, prod: program.opts().prod, resolutions: program.opts().resolutions, semverRange: program.opts().semverRange, diff --git a/src/bin-set-semver-ranges/set-semver-ranges.ts b/src/bin-set-semver-ranges/set-semver-ranges.ts index 7317947b..5610e2aa 100644 --- a/src/bin-set-semver-ranges/set-semver-ranges.ts +++ b/src/bin-set-semver-ranges/set-semver-ranges.ts @@ -9,7 +9,12 @@ export const setSemverRanges = (input: ProgramInput, disk: Disk): void => { const mismatches = listSemverGroupMismatches(semverGroup); mismatches.forEach(({ dependencyType, name, version, wrapper }) => { const root: any = wrapper.contents; - root[dependencyType][name] = setSemverRange(semverGroup.range, version); + const nextVersion = setSemverRange(semverGroup.range, version); + if (dependencyType === 'pnpmOverrides') { + root.pnpm.overrides[name] = nextVersion; + } else { + root[dependencyType][name] = nextVersion; + } }); }); diff --git a/src/constants.ts b/src/constants.ts index e7c8c02e..fe6f4c9f 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -3,6 +3,7 @@ export type DependencyType = | 'devDependencies' | 'overrides' | 'peerDependencies' + | 'pnpmOverrides' | 'resolutions' | 'workspace'; @@ -86,13 +87,17 @@ export type SyncpackConfig = Readonly<{ */ indent: string; /** - * whether to search within pnpm overrides + * whether to search within npm overrides */ overrides: boolean; /** * whether to search within peerDependencies */ peer: boolean; + /** + * whether to search within pnpm overrides + */ + pnpmOverrides: boolean; /** * whether to search within dependencies */ @@ -141,6 +146,7 @@ export const DEPENDENCY_TYPES: DependencyType[] = [ 'devDependencies', 'overrides', 'peerDependencies', + 'pnpmOverrides', 'resolutions', 'workspace', ]; @@ -179,6 +185,7 @@ export const DEFAULT_CONFIG: SyncpackConfig = { indent: ' ', overrides: true, peer: true, + pnpmOverrides: true, prod: true, resolutions: true, workspace: true, diff --git a/src/lib/get-input/get-config.ts b/src/lib/get-input/get-config.ts index 1255a357..0fb97164 100644 --- a/src/lib/get-input/get-config.ts +++ b/src/lib/get-input/get-config.ts @@ -40,6 +40,7 @@ export const getConfig = ( isBoolean(program.workspace) || isBoolean(program.overrides) || isBoolean(program.peer) || + isBoolean(program.pnpmOverrides) || isBoolean(program.prod) || isBoolean(program.resolutions); @@ -55,6 +56,9 @@ export const getConfig = ( const peer = hasTypeOverride ? Boolean(program.peer) : getOption('peer', isBoolean); + const pnpmOverrides = hasTypeOverride + ? Boolean(program.pnpmOverrides) + : getOption('pnpmOverrides', isBoolean); const prod = hasTypeOverride ? Boolean(program.prod) : getOption('prod', isBoolean); @@ -63,13 +67,20 @@ export const getConfig = ( : getOption('resolutions', isBoolean); const dependencyTypes = - dev || workspace || overrides || peer || prod || resolutions + dev || + overrides || + peer || + pnpmOverrides || + prod || + resolutions || + workspace ? DEPENDENCY_TYPES.filter( (type) => + (type === 'dependencies' && prod) || (type === 'devDependencies' && dev) || (type === 'overrides' && overrides) || (type === 'peerDependencies' && peer) || - (type === 'dependencies' && prod) || + (type === 'pnpmOverrides' && pnpmOverrides) || (type === 'resolutions' && resolutions), ) : DEPENDENCY_TYPES; @@ -110,7 +121,7 @@ export const getConfig = ( isArrayOfVersionGroups, ).concat(defaultVersionGroup); - const finalConfig = { + const finalConfig: SyncpackConfig = { dependencyTypes, dev, filter, @@ -118,6 +129,7 @@ export const getConfig = ( workspace, overrides, peer, + pnpmOverrides, prod, resolutions, semverGroups, diff --git a/src/lib/get-input/get-input.spec.ts b/src/lib/get-input/get-input.spec.ts index a880b297..4e170987 100644 --- a/src/lib/get-input/get-input.spec.ts +++ b/src/lib/get-input/get-input.spec.ts @@ -7,12 +7,13 @@ import { CWD, DEFAULT_CONFIG } from '../../constants'; describe('getInput', () => { describe('dependencyTypes', () => { const disk = mockDisk(); - const prod = 'dependencies'; const dev = 'devDependencies'; - const peer = 'peerDependencies'; - const workspace = 'workspace'; const overrides = 'overrides'; + const peer = 'peerDependencies'; + const pnpmOverrides = 'pnpmOverrides'; + const prod = 'dependencies'; const resolutions = 'resolutions'; + const workspace = 'workspace'; it('includes all except workspace (which is a not a property of package.json) if none are set', () => { expect(getInput(disk, {})).toHaveProperty( @@ -38,6 +39,10 @@ describe('getInput', () => { 'dependencyTypes', [overrides], ); + expect(getInput(disk, { pnpmOverrides: true })).toHaveProperty( + 'dependencyTypes', + [pnpmOverrides], + ); expect(getInput(disk, { resolutions: true })).toHaveProperty( 'dependencyTypes', [resolutions], diff --git a/src/lib/get-input/get-instances.ts b/src/lib/get-input/get-instances.ts index d8a78ed6..924fa1df 100644 --- a/src/lib/get-input/get-instances.ts +++ b/src/lib/get-input/get-instances.ts @@ -46,25 +46,20 @@ export function getInstances( if (dependencyType === 'workspace') { const name = wrapper.contents?.name; const version = wrapper.contents?.version; - if (!isNonEmptyString(name)) continue; - if (name.search(new RegExp(options.filter)) === -1) continue; - if (!isNonEmptyString(version)) continue; - const instance = { dependencyType, name, version, wrapper }; - allInstances.all.push(instance); - groupInstancesBy('semverGroups', dependencyType, pkgName, instance); - groupInstancesBy('versionGroups', dependencyType, pkgName, instance); + addInstance(wrapper, dependencyType, pkgName, name, version); + } else if (dependencyType === 'pnpmOverrides') { + const versionsByName = wrapper.contents?.pnpm?.overrides; + if (!isObject>(versionsByName)) continue; + const pkgs = Object.entries(versionsByName); + for (const [name, version] of pkgs) { + addInstance(wrapper, dependencyType, pkgName, name, version); + } } else { const versionsByName = wrapper.contents?.[dependencyType]; if (!isObject>(versionsByName)) continue; const pkgs = Object.entries(versionsByName); for (const [name, version] of pkgs) { - if (!isNonEmptyString(name)) continue; - if (name.search(new RegExp(options.filter)) === -1) continue; - if (!isNonEmptyString(version)) continue; - const instance = { dependencyType, name, version, wrapper }; - allInstances.all.push(instance); - groupInstancesBy('semverGroups', dependencyType, pkgName, instance); - groupInstancesBy('versionGroups', dependencyType, pkgName, instance); + addInstance(wrapper, dependencyType, pkgName, name, version); } } } @@ -72,6 +67,22 @@ export function getInstances( return allInstances; + function addInstance( + wrapper: SourceWrapper, + dependencyType: DependencyType, + pkgName: string, + name?: string, + version?: string, + ): void { + if (!isNonEmptyString(name)) return; + if (name.search(new RegExp(options.filter)) === -1) return; + if (!isNonEmptyString(version)) return; + const instance = { dependencyType, name, version, wrapper }; + allInstances.all.push(instance); + groupInstancesBy('semverGroups', dependencyType, pkgName, instance); + groupInstancesBy('versionGroups', dependencyType, pkgName, instance); + } + function withInstances(group: T): T & InstanceIndex { const instances: InstanceIndex['instances'] = []; const instancesByName: InstanceIndex['instancesByName'] = {}; diff --git a/src/lib/get-input/get-wrappers/index.ts b/src/lib/get-input/get-wrappers/index.ts index 81ba140d..c1b00d1b 100644 --- a/src/lib/get-input/get-wrappers/index.ts +++ b/src/lib/get-input/get-wrappers/index.ts @@ -15,13 +15,16 @@ export interface Source { keywords?: string[]; name?: string; peerDependencies?: Record; + pnpm?: { + overrides?: Record; + }; repository?: { type: string; url: string } | string; resolutions?: Record; scripts?: Record; version?: string; workspaces?: Record | string[]; [otherProps: string]: - | Record + | Record> | string | string[] | undefined; diff --git a/src/option.ts b/src/option.ts index 097e1887..30f61ac6 100644 --- a/src/option.ts +++ b/src/option.ts @@ -13,8 +13,9 @@ export const option = { '-i, --indent [value]', `override indentation. defaults to "${DEFAULT_CONFIG.indent}"`, ], - overrides: ['-o, --overrides', chalk`include {yellow overrides} (pnpm)`], + overrides: ['-o, --overrides', chalk`include {yellow overrides} (npm)`], peer: ['-P, --peer', chalk`include {yellow peerDependencies}`], + pnpmOverrides: ['-O, --overrides', chalk`include {yellow overrides} (pnpm)`], prod: ['-p, --prod', chalk`include {yellow dependencies}`], resolutions: [ '-R, --resolutions', diff --git a/test/mock.ts b/test/mock.ts index 23b4ad00..8a87f9c0 100644 --- a/test/mock.ts +++ b/test/mock.ts @@ -1,5 +1,5 @@ import { EOL } from 'os'; -import { join, normalize } from 'path'; +import { join } from 'path'; import { CWD } from '../src/constants'; import type { Source, SourceWrapper } from '../src/lib/get-input/get-wrappers'; @@ -16,12 +16,18 @@ export const mockPackage = ( { deps, devDeps, + overrides, peerDeps, + pnpmOverrides, + resolutions, otherProps, }: { deps?: string[]; devDeps?: string[]; + overrides?: string[]; peerDeps?: string[]; + pnpmOverrides?: string[]; + resolutions?: string[]; otherProps?: Record>; } = {}, ): SourceWrapper => { @@ -38,11 +44,28 @@ export const mockPackage = ( devDependencies: toObject(devDeps), } : {}), + ...(overrides && overrides.length > 0 + ? { + overrides: toObject(overrides), + } + : {}), ...(peerDeps && peerDeps.length > 0 ? { peerDependencies: toObject(peerDeps), } : {}), + ...(pnpmOverrides && pnpmOverrides.length > 0 + ? { + pnpm: { + overrides: toObject(pnpmOverrides), + }, + } + : {}), + ...(resolutions && resolutions.length > 0 + ? { + resolutions: toObject(resolutions), + } + : {}), ...(otherProps ? otherProps : {}), }, filePath: join(CWD, `${dirName}/package.json`), diff --git a/test/scenarios/index.ts b/test/scenarios/index.ts index b4ade99d..4dec9d9a 100644 --- a/test/scenarios/index.ts +++ b/test/scenarios/index.ts @@ -136,6 +136,7 @@ export const scenarios = { { dev: true, overrides: false, + pnpmOverrides: false, peer: false, prod: true, resolutions: false, @@ -149,25 +150,103 @@ export const scenarios = { ); }, /** - * Only `dependencies` are checked + * Only `dependencies` is unchecked * The semver range `~` should be used - * A uses exact versions for `foo` and `bar` - * A should be fixed to use `~` in both cases + * A uses exact versions for `a` + * A should be fixed to use `~` in all other cases */ semverRangesDoNotMatchConfig() { return createScenario( [ { path: 'packages/a/package.json', - before: mockPackage('a', { deps: ['foo@0.1.0', 'bar@2.0.0'] }), - after: mockPackage('a', { deps: ['foo@~0.1.0', 'bar@~2.0.0'] }), + before: mockPackage('a', { + deps: ['a@0.1.0'], + devDeps: ['b@0.1.0'], + overrides: ['c@0.1.0'], + peerDeps: ['d@0.1.0'], + resolutions: ['e@0.1.0'], + }), + after: mockPackage('a', { + deps: ['a@0.1.0'], + devDeps: ['b@~0.1.0'], + overrides: ['c@~0.1.0'], + peerDeps: ['d@~0.1.0'], + resolutions: ['e@~0.1.0'], + }), + }, + ], + { + dev: true, + overrides: true, + pnpmOverrides: false, + peer: true, + prod: false, + resolutions: true, + workspace: true, + semverRange: '~', + }, + ); + }, + /** + * A has a pnpm override of C + * B has a pnpm override of C + * The versions do not match + * The highest semver version wins + */ + dependentDoesNotMatchPnpmOverrideVersion() { + return createScenario( + [ + { + path: 'packages/a/package.json', + before: mockPackage('a', { overrides: ['c@0.1.0'] }), + after: mockPackage('a', { overrides: ['c@0.2.0'] }), + }, + { + path: 'packages/b/package.json', + before: mockPackage('b', { overrides: ['c@0.2.0'] }), + after: mockPackage('b', { overrides: ['c@0.2.0'] }), }, ], { dev: false, + overrides: true, + pnpmOverrides: false, peer: false, - prod: true, - semverRange: '~', + prod: false, + resolutions: false, + workspace: false, + }, + ); + }, + /** + * A has an npm override of C + * B has an npm override of C + * The versions do not match + * The highest semver version wins + */ + dependentDoesNotMatchNpmOverrideVersion() { + return createScenario( + [ + { + path: 'packages/a/package.json', + before: mockPackage('a', { pnpmOverrides: ['c@0.1.0'] }), + after: mockPackage('a', { pnpmOverrides: ['c@0.2.0'] }), + }, + { + path: 'packages/b/package.json', + before: mockPackage('b', { pnpmOverrides: ['c@0.2.0'] }), + after: mockPackage('b', { pnpmOverrides: ['c@0.2.0'] }), + }, + ], + { + dev: false, + overrides: false, + pnpmOverrides: true, + peer: false, + prod: false, + resolutions: false, + workspace: false, }, ); },