Skip to content

Commit 6c159a4

Browse files
authored
fix(core): don't spread plugin name into set constructor (#35385)
This pull request contains a small fix in the `build-project-graph.ts` file to correct the way `Set` is initialized for tracking in-progress plugins. The change removes the unnecessary spread operator when creating the `Set` from plugin names. * Fixed `inProgressPlugins` initialization in both `updateProjectGraphWithPlugins` and `applyProjectMetadata` functions by removing the spread operator, ensuring plugin names are correctly added to the `Set`. [[1]](diffhunk://#diff-14bd07dde50d74ec748f1bce0f9d8cf05504e36c83928d1ad077dcff088b6822L323-R323) [[2]](diffhunk://#diff-14bd07dde50d74ec748f1bce0f9d8cf05504e36c83928d1ad077dcff088b6822L441-R441)
1 parent 2f5e5b1 commit 6c159a4

4 files changed

Lines changed: 111 additions & 50 deletions

File tree

packages/nx/src/project-graph/build-project-graph.ts

Lines changed: 14 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { assertWorkspaceValidity } from '../utils/assert-workspace-validity';
1616
import { DelayedSpinner } from '../utils/delayed-spinner';
1717
import { readJsonFile } from '../utils/fileutils';
1818
import { PackageJson } from '../utils/package-json';
19+
import { formatPluginProgressText } from '../utils/plugin-progress-text';
1920
import { ProgressTopics } from '../utils/progress-topics';
2021
import { workspaceRoot } from '../utils/workspace-root';
2122
import {
@@ -320,25 +321,16 @@ async function updateProjectGraphWithPlugins(
320321

321322
let spinner: DelayedSpinner;
322323
const inProgressPlugins = new Set<string>(
323-
...createDependencyPlugins.map((plugin) => plugin.name)
324+
createDependencyPlugins.map((plugin) => plugin.name)
324325
);
325326

326-
function getSpinnerText() {
327-
if (!spinner || inProgressPlugins.size === 0) {
328-
return '';
329-
}
330-
331-
if (inProgressPlugins.size === 1) {
332-
return `Creating project graph dependencies with ${
333-
inProgressPlugins.values().next().value
334-
}`;
335-
} else {
336-
return [
337-
`Creating project graph dependencies with ${inProgressPlugins.size} plugins`,
338-
...Array.from(inProgressPlugins).map((p) => ` - ${p}`),
339-
].join('\n');
340-
}
341-
}
327+
const getSpinnerText = () =>
328+
spinner
329+
? formatPluginProgressText(
330+
'Creating project graph dependencies',
331+
inProgressPlugins
332+
)
333+
: '';
342334

343335
spinner = new DelayedSpinner(getSpinnerText(), {
344336
progressTopic: ProgressTopics.GraphConstruction,
@@ -438,25 +430,13 @@ export async function applyProjectMetadata(
438430
);
439431

440432
const inProgressPlugins = new Set<string>(
441-
...createMetadataPlugins.map((p) => p.name)
433+
createMetadataPlugins.map((p) => p.name)
442434
);
443435

444-
function getSpinnerText() {
445-
if (!spinner || inProgressPlugins.size === 0) {
446-
return '';
447-
}
448-
449-
if (inProgressPlugins.size === 1) {
450-
return `Creating project metadata with ${
451-
inProgressPlugins.values().next().value
452-
}`;
453-
} else {
454-
return [
455-
`Creating project metadata with ${inProgressPlugins.size} plugins`,
456-
...Array.from(inProgressPlugins).map((p) => ` - ${p}`),
457-
].join('\n');
458-
}
459-
}
436+
const getSpinnerText = () =>
437+
spinner
438+
? formatPluginProgressText('Creating project metadata', inProgressPlugins)
439+
: '';
460440

461441
spinner = createMetadataPlugins.length
462442
? new DelayedSpinner(getSpinnerText(), {

packages/nx/src/project-graph/utils/project-configuration-utils.ts

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { minimatch } from 'minimatch';
1212
import { performance } from 'perf_hooks';
1313

1414
import { DelayedSpinner } from '../../utils/delayed-spinner';
15+
import { formatPluginProgressText } from '../../utils/plugin-progress-text';
1516
import { ProgressTopics } from '../../utils/progress-topics';
1617
import {
1718
AggregateCreateNodesError,
@@ -84,22 +85,13 @@ export async function createProjectConfigurationsWithPlugins(
8485
let spinner: DelayedSpinner;
8586
const inProgressPlugins = new Set<string>();
8687

87-
function getSpinnerText() {
88-
if (!spinner || inProgressPlugins.size === 0) {
89-
return '';
90-
}
91-
92-
if (inProgressPlugins.size === 1) {
93-
return `Creating project graph nodes with ${
94-
inProgressPlugins.values().next().value
95-
}`;
96-
} else {
97-
return [
98-
`Creating project graph nodes with ${inProgressPlugins.size} plugins`,
99-
...Array.from(inProgressPlugins).map((p) => ` - ${p}`),
100-
].join('\n');
101-
}
102-
}
88+
const getSpinnerText = () =>
89+
spinner
90+
? formatPluginProgressText(
91+
'Creating project graph nodes',
92+
inProgressPlugins
93+
)
94+
: '';
10395

10496
const createNodesPlugins = plugins.filter(
10597
(plugin) => plugin.createNodes?.[0]
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { formatPluginProgressText } from './plugin-progress-text';
2+
3+
describe('formatPluginProgressText', () => {
4+
it('returns an empty string when no plugins are in progress', () => {
5+
expect(
6+
formatPluginProgressText('Creating project metadata', new Set())
7+
).toBe('');
8+
});
9+
10+
it('returns a single-line message when exactly one plugin is in progress', () => {
11+
const inProgress = new Set(['@nx/rust']);
12+
expect(
13+
formatPluginProgressText(
14+
'Creating project graph dependencies',
15+
inProgress
16+
)
17+
).toBe('Creating project graph dependencies with @nx/rust');
18+
});
19+
20+
it('returns a multi-line message listing each plugin when multiple are in progress', () => {
21+
const inProgress = new Set(['@nx/rust', '@nx/js', '@nx/eslint']);
22+
expect(
23+
formatPluginProgressText(
24+
'Creating project graph dependencies',
25+
inProgress
26+
)
27+
).toBe(
28+
[
29+
'Creating project graph dependencies with 3 plugins',
30+
' - @nx/rust',
31+
' - @nx/js',
32+
' - @nx/eslint',
33+
].join('\n')
34+
);
35+
});
36+
37+
it('preserves each plugin name as a whole entry, not per-character', () => {
38+
// Regression: `new Set(...array)` spreads into the Set constructor,
39+
// which takes a single iterable — for a string that means iterating
40+
// character-by-character. The formatter must surface full plugin names.
41+
const inProgress = new Set(['@nx/rust']);
42+
const output = formatPluginProgressText(
43+
'Creating project graph dependencies',
44+
inProgress
45+
);
46+
expect(output).toContain('@nx/rust');
47+
expect(output).not.toMatch(/^\s*- @$/m);
48+
expect(output).not.toMatch(/^\s*- r$/m);
49+
});
50+
51+
it('does not mutate the provided set', () => {
52+
const inProgress = new Set(['@nx/rust', '@nx/js']);
53+
formatPluginProgressText('Creating project metadata', inProgress);
54+
expect(inProgress.size).toBe(2);
55+
});
56+
57+
it('reflects the current set contents across successive calls', () => {
58+
const inProgress = new Set(['@nx/rust', '@nx/js']);
59+
const before = formatPluginProgressText(
60+
'Creating project metadata',
61+
inProgress
62+
);
63+
inProgress.delete('@nx/rust');
64+
const after = formatPluginProgressText(
65+
'Creating project metadata',
66+
inProgress
67+
);
68+
69+
expect(before).toContain('2 plugins');
70+
expect(after).toBe('Creating project metadata with @nx/js');
71+
});
72+
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
export function formatPluginProgressText(
2+
action: string,
3+
inProgressPlugins: ReadonlySet<string>
4+
): string {
5+
if (inProgressPlugins.size === 0) {
6+
return '';
7+
}
8+
9+
if (inProgressPlugins.size === 1) {
10+
return `${action} with ${inProgressPlugins.values().next().value}`;
11+
}
12+
13+
return [
14+
`${action} with ${inProgressPlugins.size} plugins`,
15+
...Array.from(inProgressPlugins, (p) => ` - ${p}`),
16+
].join('\n');
17+
}

0 commit comments

Comments
 (0)