Skip to content

Commit 86fbeac

Browse files
MaxKlessFrozenPandaz
authored andcommitted
fix(core): hide already-installed nx packages from suggestion list during nx import (#34227)
## Current Behavior If something is in `package.json#dependencies`, we still suggest it to be `nx add`-ed during `nx import` ## Expected Behavior If a plugin is already installed, we don't suggest it anymore (cherry picked from commit f89ccb0)
1 parent a40d06e commit 86fbeac

3 files changed

Lines changed: 100 additions & 1 deletion

File tree

packages/nx/src/command-line/import/import.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { output } from '../../utils/output';
99
const createSpinner = require('ora');
1010
import { detectPlugins } from '../init/init-v2';
1111
import { readNxJson } from '../../config/nx-json';
12+
import { readJsonFile } from '../../utils/fileutils';
13+
import { PackageJson } from '../../utils/package-json';
1214
import { workspaceRoot } from '../../utils/workspace-root';
1315
import {
1416
addPackagePathToWorkspaces,
@@ -263,8 +265,15 @@ export async function importHandler(options: ImportOptions) {
263265

264266
resetWorkspaceContext();
265267

268+
let packageJson: PackageJson | null;
269+
try {
270+
packageJson = readJsonFile<PackageJson>('package.json');
271+
} catch {
272+
packageJson = null;
273+
}
266274
const { plugins, updatePackageScripts } = await detectPlugins(
267275
nxJson,
276+
packageJson,
268277
options.interactive,
269278
true
270279
);
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { detectPlugins } from './init-v2';
2+
3+
// Mock dependencies
4+
jest.mock('fs', () => ({
5+
...jest.requireActual('fs'),
6+
existsSync: jest.fn((path: string) => {
7+
if (path === 'package.json') return true;
8+
return false;
9+
}),
10+
}));
11+
12+
jest.mock('../../utils/fileutils', () => ({
13+
readJsonFile: jest.fn(),
14+
fileExists: jest.fn(() => false),
15+
}));
16+
17+
import { readJsonFile } from '../../utils/fileutils';
18+
const mockReadJsonFile = readJsonFile as jest.Mock;
19+
20+
jest.mock('../../utils/workspace-context', () => ({
21+
globWithWorkspaceContextSync: jest.fn(() => []),
22+
}));
23+
24+
jest.mock('../../utils/output', () => ({
25+
output: { log: jest.fn() },
26+
}));
27+
28+
describe('detectPlugins', () => {
29+
beforeEach(() => {
30+
jest.clearAllMocks();
31+
});
32+
33+
it('should not suggest a plugin that is already installed as an npm dependency', async () => {
34+
const packageJson = {
35+
name: 'test',
36+
version: '1.0.0',
37+
dependencies: { vite: '^5.0.0' },
38+
devDependencies: { '@nx/vite': '19.0.0' },
39+
};
40+
mockReadJsonFile.mockReturnValue(packageJson);
41+
42+
const result = await detectPlugins({ plugins: [] }, packageJson, false);
43+
44+
expect(result.plugins).not.toContain('@nx/vite');
45+
});
46+
47+
it('should suggest a plugin when the tool is installed but the nx plugin is not', async () => {
48+
const packageJson = {
49+
name: 'test',
50+
version: '1.0.0',
51+
dependencies: { vite: '^5.0.0' },
52+
devDependencies: {},
53+
};
54+
mockReadJsonFile.mockReturnValue(packageJson);
55+
56+
const result = await detectPlugins({ plugins: [] }, packageJson, false);
57+
58+
expect(result.plugins).toContain('@nx/vite');
59+
});
60+
61+
it('should not suggest a plugin already listed in nx.json plugins', async () => {
62+
const packageJson = {
63+
name: 'test',
64+
version: '1.0.0',
65+
dependencies: { vite: '^5.0.0' },
66+
devDependencies: {},
67+
};
68+
mockReadJsonFile.mockReturnValue(packageJson);
69+
70+
const result = await detectPlugins(
71+
{ plugins: ['@nx/vite'] },
72+
packageJson,
73+
false
74+
);
75+
76+
expect(result.plugins).not.toContain('@nx/vite');
77+
});
78+
});

packages/nx/src/command-line/init/init-v2.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ async function initHandlerImpl(options: InitArgs): Promise<void> {
183183
updatePackageScripts = true;
184184
} else {
185185
const { plugins: _plugins, updatePackageScripts: _updatePackageScripts } =
186-
await detectPlugins(nxJson, options.interactive);
186+
await detectPlugins(nxJson, packageJson, options.interactive);
187187
plugins = _plugins;
188188
updatePackageScripts = _updatePackageScripts;
189189
}
@@ -273,6 +273,7 @@ const npmPackageToPluginMap: Record<string, `@nx/${string}`> = {
273273

274274
export async function detectPlugins(
275275
nxJson: NxJsonConfiguration,
276+
packageJson: PackageJson | null,
276277
interactive: boolean,
277278
includeAngularCli?: boolean
278279
): Promise<{
@@ -290,6 +291,17 @@ export async function detectPlugins(
290291
})
291292
);
292293

294+
// Also treat already-installed @nx/* and @nrwl/* packages as current plugins
295+
const rootDeps = {
296+
...packageJson?.dependencies,
297+
...packageJson?.devDependencies,
298+
};
299+
for (const dep of Object.keys(rootDeps)) {
300+
if (dep.startsWith('@nx/') || dep.startsWith('@nrwl/')) {
301+
currentPlugins.add(getPackageNameFromImportPath(dep));
302+
}
303+
}
304+
293305
const detectedPlugins = new Set<string>();
294306
for (const file of files) {
295307
if (!existsSync(file)) continue;

0 commit comments

Comments
 (0)