Skip to content

Commit

Permalink
feat(pnpm): add support for pnpm overrides
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
JamieMason committed May 31, 2022
1 parent 61e831d commit 2d1bf05
Show file tree
Hide file tree
Showing 19 changed files with 248 additions and 47 deletions.
28 changes: 17 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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> path to a syncpack config file
Expand Down Expand Up @@ -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 <range> see supported ranges below. defaults to ""
-c, --config <path> path to a syncpack config file
Expand Down Expand Up @@ -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> path to a syncpack config file
-w, --workspace include locally developed package versions
Expand Down Expand Up @@ -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> path to a syncpack config file
-w, --workspace include locally developed package versions
Expand Down Expand Up @@ -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 <range> see supported ranges below. defaults to ""
Expand Down Expand Up @@ -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,
Expand All @@ -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`

Expand Down
24 changes: 24 additions & 0 deletions src/bin-fix-mismatches/fix-mismatches.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
]);
});
});
});
6 changes: 5 additions & 1 deletion src/bin-fix-mismatches/fix-mismatches.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
});
}
Expand Down
1 change: 1 addition & 0 deletions src/bin-fix-mismatches/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions src/bin-lint-semver-ranges/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
6 changes: 4 additions & 2 deletions src/bin-lint-semver-ranges/lint-semver-ranges.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'],
]);
});
});
1 change: 1 addition & 0 deletions src/bin-list-mismatches/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions src/bin-list/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
16 changes: 15 additions & 1 deletion src/bin-list/list.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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([
Expand All @@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/bin-set-semver-ranges/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
7 changes: 6 additions & 1 deletion src/bin-set-semver-ranges/set-semver-ranges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
});
});

Expand Down
9 changes: 8 additions & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export type DependencyType =
| 'devDependencies'
| 'overrides'
| 'peerDependencies'
| 'pnpmOverrides'
| 'resolutions'
| 'workspace';

Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -141,6 +146,7 @@ export const DEPENDENCY_TYPES: DependencyType[] = [
'devDependencies',
'overrides',
'peerDependencies',
'pnpmOverrides',
'resolutions',
'workspace',
];
Expand Down Expand Up @@ -179,6 +185,7 @@ export const DEFAULT_CONFIG: SyncpackConfig = {
indent: ' ',
overrides: true,
peer: true,
pnpmOverrides: true,
prod: true,
resolutions: true,
workspace: true,
Expand Down
18 changes: 15 additions & 3 deletions src/lib/get-input/get-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -55,6 +56,9 @@ export const getConfig = (
const peer = hasTypeOverride
? Boolean(program.peer)
: getOption<boolean>('peer', isBoolean);
const pnpmOverrides = hasTypeOverride
? Boolean(program.pnpmOverrides)
: getOption<boolean>('pnpmOverrides', isBoolean);
const prod = hasTypeOverride
? Boolean(program.prod)
: getOption<boolean>('prod', isBoolean);
Expand All @@ -63,13 +67,20 @@ export const getConfig = (
: getOption<boolean>('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;
Expand Down Expand Up @@ -110,14 +121,15 @@ export const getConfig = (
isArrayOfVersionGroups,
).concat(defaultVersionGroup);

const finalConfig = {
const finalConfig: SyncpackConfig = {
dependencyTypes,
dev,
filter,
indent,
workspace,
overrides,
peer,
pnpmOverrides,
prod,
resolutions,
semverGroups,
Expand Down
11 changes: 8 additions & 3 deletions src/lib/get-input/get-input.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -38,6 +39,10 @@ describe('getInput', () => {
'dependencyTypes',
[overrides],
);
expect(getInput(disk, { pnpmOverrides: true })).toHaveProperty(
'dependencyTypes',
[pnpmOverrides],
);
expect(getInput(disk, { resolutions: true })).toHaveProperty(
'dependencyTypes',
[resolutions],
Expand Down
Loading

0 comments on commit 2d1bf05

Please sign in to comment.