Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions packages/cli/src/migration/__tests__/migrator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2063,6 +2063,40 @@ describe('ensureVitePlusBootstrap', () => {
expect(detectVitePlusBootstrapPending(tmpDir, PackageManager.npm)).toBe(false);
});

it('tolerates nested npm override objects under managed keys', () => {
fs.writeFileSync(
path.join(tmpDir, 'package.json'),
JSON.stringify({
name: 'test',
devDependencies: { 'vite-plus': 'latest' },
// Nested npm override objects: user overrides scoped UNDER the managed
// keys. Neither aliases the package itself.
overrides: {
vite: { rollup: '^4.0.0' },
vitest: { '@vitest/expect': '4.0.0' },
},
devEngines: {
packageManager: { name: 'npm', version: '10.33.0', onFail: 'download' },
},
}),
);

expect(detectVitePlusBootstrapPending(tmpDir, PackageManager.npm)).toBe(true);
const result = ensureVitePlusBootstrap(makeWorkspaceInfo(tmpDir, PackageManager.npm));

expect(result.changed).toBe(true);
expect(detectVitePlusBootstrapPending(tmpDir, PackageManager.npm)).toBe(false);
const pkg = readJson(path.join(tmpDir, 'package.json')) as {
overrides: Record<string, unknown>;
};
// The nested object under `vite` does not satisfy the managed alias, so it
// is replaced with the managed override.
expect(pkg.overrides.vite).toContain('@voidzero-dev/vite-plus-core');
// The project does NOT use vitest directly, so `vitest` is not managed; the
// user override scoped under it is left intact rather than removed.
expect(pkg.overrides.vitest).toEqual({ '@vitest/expect': '4.0.0' });
});

it('replaces protocol-pinned migration targets in force-override mode', () => {
const savedForceMigrate = process.env.VP_FORCE_MIGRATE;
process.env.VP_FORCE_MIGRATE = '1';
Expand Down
5 changes: 4 additions & 1 deletion packages/cli/src/migration/migrator/vite-plus-bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,10 @@ function overrideSpecSatisfiesVitePlus(
spec: string | undefined,
catalogDependencyResolver?: CatalogDependencyResolver,
): boolean {
if (!spec) {
// npm/bun `overrides` may hold a nested object value — a user override scoped
// UNDER the dependency (e.g. `{"vite": {"rollup": "..."}}`) that does not
// alias the dependency itself, so it never satisfies the managed override.
if (typeof spec !== 'string' || !spec) {
return false;
Comment on lines +130 to 131

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve nested override children when adding the managed alias

When overrides.vite uses npm's long object form, returning false here makes ensureOverrideEntries replace the entire object with a string, silently deleting child overrides such as the test's rollup pin. It also misclassifies { ".": "npm:@voidzero-dev/vite-plus-core@latest", "rollup": "^4" }, even though the managed alias is already satisfied. The npm 11.4.2 package-json documentation explicitly defines this form as supporting both the package itself via "." and its children, so migration should preserve the object and inspect or set its "." member rather than collapsing it.

Useful? React with 👍 / 👎.

}
if (isSemanticVitePlusOverrideSpec(dependencyName, spec)) {
Expand Down
Loading