Skip to content

Commit bdd27bc

Browse files
committed
fix(migration): preserve custom pnpm overrides and peerDependencyRules
Move any remaining non-Vite pnpm.overrides from package.json to pnpm-workspace.yaml instead of leaving them behind. pnpm ignores workspace-level overrides when pnpm.overrides exists in package.json, even with unrelated entries, so all overrides must live in the workspace yaml. Also surgically remove only Vite-managed entries from pnpm.peerDependencyRules instead of deleting the entire object, preserving any custom rules the project already had.
1 parent c31caa6 commit bdd27bc

2 files changed

Lines changed: 117 additions & 23 deletions

File tree

  • packages/cli
    • snap-tests-global/migration-monorepo-pnpm-overrides-dependency-selector
    • src/migration

packages/cli/snap-tests-global/migration-monorepo-pnpm-overrides-dependency-selector/snap.txt

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,7 @@ export default defineConfig({
3131
"vite": "catalog:",
3232
"vite-plus": "catalog:"
3333
},
34-
"packageManager": "pnpm@<semver>+sha512.cf9998222162dd85864d0a8102e7892e7ba4ceadebbf5a31f9c2fce48dfce317a9c53b9f6464d1ef9042cba2e02ae02a9f7c143a2b438cd93c91840f0192b9dd",
35-
"pnpm": {
36-
"overrides": {
37-
"react-click-away-listener>react": "0.0.0-experimental-7dc903cd-20251203",
38-
"supertest>superagent": "9.0.2"
39-
}
40-
}
34+
"packageManager": "pnpm@<semver>+sha512.cf9998222162dd85864d0a8102e7892e7ba4ceadebbf5a31f9c2fce48dfce317a9c53b9f6464d1ef9042cba2e02ae02a9f7c143a2b438cd93c91840f0192b9dd"
4135
}
4236

4337
> cat pnpm-workspace.yaml # check pnpm-workspace.yaml
@@ -54,6 +48,7 @@ overrides:
5448
'supertest>superagent': '9.0.2'
5549
vite: 'catalog:'
5650
vitest: 'catalog:'
51+
react-click-away-listener>react: <semver>
5752
peerDependencyRules:
5853
allowAny:
5954
- vite

packages/cli/src/migration/migrator.ts

Lines changed: 115 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,7 @@ export function rewriteStandaloneProject(
700700
rewritePnpmWorkspaceYaml(projectPath);
701701
}
702702
let extractedStagedConfig: Record<string, string | string[]> | null = null;
703+
let remainingPnpmOverrides: Record<string, string> | undefined;
703704
editJsonFile<{
704705
overrides?: Record<string, string>;
705706
resolutions?: Record<string, string>;
@@ -739,22 +740,40 @@ export function rewriteStandaloneProject(
739740
},
740741
};
741742
} else {
742-
// pnpm uses overrides/peerDependencyRules from pnpm-workspace.yaml
743-
// clean up any existing entries from package.json
743+
// Remove Vite-managed keys from pnpm.overrides
744744
for (const key of [...overrideKeys, ...REMOVE_PACKAGES]) {
745745
if (pkg.pnpm?.overrides?.[key]) {
746746
delete pkg.pnpm.overrides[key];
747747
}
748748
}
749-
delete pkg.pnpm?.peerDependencyRules;
750-
if (pkg.pnpm?.overrides && Object.keys(pkg.pnpm.overrides).length === 0) {
751-
delete pkg.pnpm.overrides;
749+
// Remove dependency selectors targeting vite
750+
for (const key in pkg.pnpm?.overrides) {
751+
if (key.includes('>')) {
752+
const splits = key.split('>');
753+
if (splits[splits.length - 1].trim() === 'vite') {
754+
delete pkg.pnpm.overrides[key];
755+
}
756+
}
757+
}
758+
// Move any remaining overrides to pnpm-workspace.yaml then delete all
759+
// (pnpm ignores workspace-level overrides when pnpm.overrides exists in package.json)
760+
if (pkg.pnpm?.overrides && Object.keys(pkg.pnpm.overrides).length > 0) {
761+
remainingPnpmOverrides = { ...pkg.pnpm.overrides };
762+
}
763+
delete pkg.pnpm?.overrides;
764+
// Only remove Vite-managed peerDependencyRules entries, preserve custom ones
765+
cleanupPeerDependencyRules(pkg.pnpm?.peerDependencyRules, overrideKeys);
766+
if (
767+
pkg.pnpm?.peerDependencyRules &&
768+
Object.keys(pkg.pnpm.peerDependencyRules).length === 0
769+
) {
770+
delete pkg.pnpm.peerDependencyRules;
752771
}
753772
if (pkg.pnpm && Object.keys(pkg.pnpm).length === 0) {
754773
delete pkg.pnpm;
755774
}
756775
}
757-
// remove dependency selector from vite, e.g. "vite-plugin-svgr>vite"
776+
// remove dependency selector from vite in force-override mode
758777
for (const key in pkg.pnpm?.overrides) {
759778
if (key.includes('>')) {
760779
const splits = key.split('>');
@@ -784,6 +803,11 @@ export function rewriteStandaloneProject(
784803
return pkg;
785804
});
786805

806+
// Move remaining non-Vite pnpm.overrides to pnpm-workspace.yaml
807+
if (remainingPnpmOverrides) {
808+
migratePnpmOverridesToWorkspaceYaml(projectPath, remainingPnpmOverrides);
809+
}
810+
787811
// Merge extracted staged config into vite.config.ts, then remove lint-staged from package.json
788812
if (extractedStagedConfig) {
789813
if (mergeStagedConfigToViteConfig(projectPath, extractedStagedConfig, silent, report)) {
@@ -983,6 +1007,55 @@ function rewritePnpmWorkspaceYaml(projectPath: string): void {
9831007
});
9841008
}
9851009

1010+
/**
1011+
* Move remaining non-Vite pnpm.overrides from package.json to pnpm-workspace.yaml.
1012+
* pnpm ignores workspace-level overrides when pnpm.overrides exists in package.json,
1013+
* so all overrides must live in pnpm-workspace.yaml.
1014+
*/
1015+
function migratePnpmOverridesToWorkspaceYaml(
1016+
projectPath: string,
1017+
overrides: Record<string, string>,
1018+
): void {
1019+
const pnpmWorkspaceYamlPath = path.join(projectPath, 'pnpm-workspace.yaml');
1020+
editYamlFile(pnpmWorkspaceYamlPath, (doc) => {
1021+
for (const [key, value] of Object.entries(overrides)) {
1022+
if (!doc.hasIn(['overrides', key])) {
1023+
doc.setIn(['overrides', scalarString(key)], scalarString(value));
1024+
}
1025+
}
1026+
});
1027+
}
1028+
1029+
/**
1030+
* Remove only Vite-managed entries from peerDependencyRules, preserving custom ones.
1031+
*/
1032+
function cleanupPeerDependencyRules(
1033+
peerDependencyRules:
1034+
| { allowAny?: string[]; allowedVersions?: Record<string, string> }
1035+
| undefined,
1036+
overrideKeys: string[],
1037+
): void {
1038+
if (!peerDependencyRules) {
1039+
return;
1040+
}
1041+
if (Array.isArray(peerDependencyRules.allowAny)) {
1042+
peerDependencyRules.allowAny = peerDependencyRules.allowAny.filter(
1043+
(key) => !overrideKeys.includes(key),
1044+
);
1045+
if (peerDependencyRules.allowAny.length === 0) {
1046+
delete peerDependencyRules.allowAny;
1047+
}
1048+
}
1049+
if (peerDependencyRules.allowedVersions) {
1050+
for (const key of overrideKeys) {
1051+
delete peerDependencyRules.allowedVersions[key];
1052+
}
1053+
if (Object.keys(peerDependencyRules.allowedVersions).length === 0) {
1054+
delete peerDependencyRules.allowedVersions;
1055+
}
1056+
}
1057+
}
1058+
9861059
/**
9871060
* Rewrite .yarnrc.yml to add vite-plus dependencies
9881061
* @param projectPath - The path to the project
@@ -1085,13 +1158,17 @@ function rewriteRootWorkspacePackageJson(
10851158
return;
10861159
}
10871160

1161+
let remainingPnpmOverrides: Record<string, string> | undefined;
10881162
editJsonFile<{
10891163
resolutions?: Record<string, string>;
10901164
overrides?: Record<string, string>;
10911165
devDependencies?: Record<string, string>;
10921166
pnpm?: {
10931167
overrides?: Record<string, string>;
1094-
peerDependencyRules?: Record<string, unknown>;
1168+
peerDependencyRules?: {
1169+
allowAny?: string[];
1170+
allowedVersions?: Record<string, string>;
1171+
};
10951172
};
10961173
}>(packageJsonPath, (pkg) => {
10971174
if (packageManager === PackageManager.yarn) {
@@ -1109,6 +1186,7 @@ function rewriteRootWorkspacePackageJson(
11091186
} else if (packageManager === PackageManager.bun) {
11101187
// bun overrides are handled in rewriteBunCatalog() with catalog: references
11111188
} else if (packageManager === PackageManager.pnpm) {
1189+
const overrideKeys = Object.keys(VITE_PLUS_OVERRIDE_PACKAGES);
11121190
if (isForceOverrideMode()) {
11131191
// In force-override mode, keep overrides in package.json pnpm.overrides
11141192
// because pnpm ignores pnpm-workspace.yaml overrides when pnpm.overrides
@@ -1122,27 +1200,43 @@ function rewriteRootWorkspacePackageJson(
11221200
},
11231201
};
11241202
} else {
1125-
// pnpm use overrides field at pnpm-workspace.yaml
1126-
// so we don't need to set overrides field at package.json
1127-
// remove packages from `resolutions` field and `pnpm.overrides` field if they exist
1128-
// https://pnpm.io/9.x/package_json#resolutions
1129-
for (const key of [...Object.keys(VITE_PLUS_OVERRIDE_PACKAGES), ...REMOVE_PACKAGES]) {
1203+
// Remove Vite-managed keys from pnpm.overrides
1204+
for (const key of [...overrideKeys, ...REMOVE_PACKAGES]) {
11301205
if (pkg.pnpm?.overrides?.[key]) {
11311206
delete pkg.pnpm.overrides[key];
11321207
}
11331208
if (pkg.resolutions?.[key]) {
11341209
delete pkg.resolutions[key];
11351210
}
11361211
}
1137-
delete pkg.pnpm?.peerDependencyRules;
1138-
if (pkg.pnpm?.overrides && Object.keys(pkg.pnpm.overrides).length === 0) {
1139-
delete pkg.pnpm.overrides;
1212+
// Remove dependency selectors targeting vite
1213+
for (const key in pkg.pnpm?.overrides) {
1214+
if (key.includes('>')) {
1215+
const splits = key.split('>');
1216+
if (splits[splits.length - 1].trim() === 'vite') {
1217+
delete pkg.pnpm.overrides[key];
1218+
}
1219+
}
1220+
}
1221+
// Move any remaining overrides to pnpm-workspace.yaml then delete all
1222+
// (pnpm ignores workspace-level overrides when pnpm.overrides exists in package.json)
1223+
if (pkg.pnpm?.overrides && Object.keys(pkg.pnpm.overrides).length > 0) {
1224+
remainingPnpmOverrides = { ...pkg.pnpm.overrides };
1225+
}
1226+
delete pkg.pnpm?.overrides;
1227+
// Only remove Vite-managed peerDependencyRules entries, preserve custom ones
1228+
cleanupPeerDependencyRules(pkg.pnpm?.peerDependencyRules, overrideKeys);
1229+
if (
1230+
pkg.pnpm?.peerDependencyRules &&
1231+
Object.keys(pkg.pnpm.peerDependencyRules).length === 0
1232+
) {
1233+
delete pkg.pnpm.peerDependencyRules;
11401234
}
11411235
if (pkg.pnpm && Object.keys(pkg.pnpm).length === 0) {
11421236
delete pkg.pnpm;
11431237
}
11441238
}
1145-
// remove dependency selector from vite, e.g. "vite-plugin-svgr>vite": "npm:vite@7.0.12"
1239+
// remove dependency selector from vite in force-override mode
11461240
for (const key in pkg.pnpm?.overrides) {
11471241
if (key.includes('>')) {
11481242
const splits = key.split('>');
@@ -1166,6 +1260,11 @@ function rewriteRootWorkspacePackageJson(
11661260
return pkg;
11671261
});
11681262

1263+
// Move remaining non-Vite pnpm.overrides to pnpm-workspace.yaml
1264+
if (remainingPnpmOverrides) {
1265+
migratePnpmOverridesToWorkspaceYaml(projectPath, remainingPnpmOverrides);
1266+
}
1267+
11691268
// rewrite package.json
11701269
rewriteMonorepoProject(projectPath, packageManager, skipStagedMigration);
11711270
}

0 commit comments

Comments
 (0)