Skip to content

Commit 3b7dc5e

Browse files
committed
fix(core): handle self referencing workspaces field (#5917)
**What's the problem this PR addresses?** `Workspace.getRecursiveWorkspaceChildren` throws `Range Error: Maximum call stack size exceeded` when it encounters a workspace more than once. Fixes #5799 **How did you fix it?** Use a `Set` to keep track of the workspaces that have been found. **Checklist** - [x] I have read the [Contributing Guide](https://yarnpkg.com/advanced/contributing). - [x] I have set the packages that need to be released for my changes to be effective. - [x] I will check that all automated PR checks pass before the PR gets reviewed. (cherry picked from commit 32979f3)
1 parent cbc0905 commit 3b7dc5e

File tree

4 files changed

+80
-20
lines changed

4 files changed

+80
-20
lines changed

.yarn/versions/4b66efea.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
releases:
2+
"@yarnpkg/cli": patch
3+
"@yarnpkg/core": patch
4+
5+
declined:
6+
- "@yarnpkg/plugin-compat"
7+
- "@yarnpkg/plugin-constraints"
8+
- "@yarnpkg/plugin-dlx"
9+
- "@yarnpkg/plugin-essentials"
10+
- "@yarnpkg/plugin-exec"
11+
- "@yarnpkg/plugin-file"
12+
- "@yarnpkg/plugin-git"
13+
- "@yarnpkg/plugin-github"
14+
- "@yarnpkg/plugin-http"
15+
- "@yarnpkg/plugin-init"
16+
- "@yarnpkg/plugin-interactive-tools"
17+
- "@yarnpkg/plugin-link"
18+
- "@yarnpkg/plugin-nm"
19+
- "@yarnpkg/plugin-npm"
20+
- "@yarnpkg/plugin-npm-cli"
21+
- "@yarnpkg/plugin-pack"
22+
- "@yarnpkg/plugin-patch"
23+
- "@yarnpkg/plugin-pnp"
24+
- "@yarnpkg/plugin-pnpm"
25+
- "@yarnpkg/plugin-stage"
26+
- "@yarnpkg/plugin-typescript"
27+
- "@yarnpkg/plugin-version"
28+
- "@yarnpkg/plugin-workspace-tools"
29+
- "@yarnpkg/builder"
30+
- "@yarnpkg/doctor"
31+
- "@yarnpkg/extensions"
32+
- "@yarnpkg/nm"
33+
- "@yarnpkg/pnpify"
34+
- "@yarnpkg/sdks"

packages/acceptance-tests/pkg-tests-specs/sources/commands/workspaces/__snapshots__/foreach.test.js.snap

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ Object {
4040
"stdout": "Test Workspace B
4141
Test Workspace C
4242
Test Workspace D
43-
Test Workspace E
4443
Test Workspace F
4544
Test Workspace G
45+
Test Workspace E
4646
Done
4747
",
4848
}
@@ -55,9 +55,9 @@ Object {
5555
"stdout": "Test Workspace B
5656
Test Workspace C
5757
Test Workspace D
58-
Test Workspace E
5958
Test Workspace F
6059
Test Workspace G
60+
Test Workspace E
6161
Done
6262
",
6363
}
@@ -146,17 +146,17 @@ Object {
146146
[workspace-d]: Test Workspace D
147147
[workspace-d]: Process exited (exit code 0)
148148
149-
[workspace-e]: Process started
150-
[workspace-e]: Test Workspace E
151-
[workspace-e]: Process exited (exit code 0)
152-
153149
[workspace-f]: Process started
154150
[workspace-f]: Test Workspace F
155151
[workspace-f]: Process exited (exit code 0)
156152
157153
[workspace-g]: Process started
158154
[workspace-g]: Test Workspace G
159155
[workspace-g]: Process exited (exit code 0)
156+
157+
[workspace-e]: Process started
158+
[workspace-e]: Test Workspace E
159+
[workspace-e]: Process exited (exit code 0)
160160
Done
161161
",
162162
}
@@ -170,9 +170,9 @@ Object {
170170
Test Workspace B
171171
Test Workspace C
172172
Test Workspace D
173-
Test Workspace E
174173
Test Workspace F
175174
Test Workspace G
175+
Test Workspace E
176176
Done
177177
",
178178
}
@@ -186,9 +186,9 @@ Object {
186186
Test Workspace B
187187
Test Workspace C
188188
Test Workspace D
189-
Test Workspace E
190189
Test Workspace F
191190
Test Workspace G
191+
Test Workspace E
192192
Done
193193
",
194194
}
@@ -230,17 +230,17 @@ Object {
230230
[workspace-d]: Test Workspace D
231231
[workspace-d]: Process exited (exit code 0)
232232
233-
[workspace-e]: Process started
234-
[workspace-e]: Test Workspace E
235-
[workspace-e]: Process exited (exit code 0)
236-
237233
[workspace-f]: Process started
238234
[workspace-f]: Test Workspace F
239235
[workspace-f]: Process exited (exit code 0)
240236
241237
[workspace-g]: Process started
242238
[workspace-g]: Test Workspace G
243239
[workspace-g]: Process exited (exit code 0)
240+
241+
[workspace-e]: Process started
242+
[workspace-e]: Test Workspace E
243+
[workspace-e]: Process exited (exit code 0)
244244
Done
245245
",
246246
}
@@ -269,9 +269,9 @@ Object {
269269
"stderr": "",
270270
"stdout": "Test Workspace C
271271
Test Workspace D
272-
Test Workspace E
273272
Test Workspace F
274273
Test Workspace G
274+
Test Workspace E
275275
Done
276276
",
277277
}
@@ -303,9 +303,9 @@ packages/workspace-a
303303
packages/workspace-b
304304
packages/workspace-c
305305
packages/workspace-c/packages/workspace-d
306-
packages/workspace-c/packages/workspace-d/packages/workspace-e
307306
packages/workspace-c/packages/workspace-f
308307
packages/workspace-c/packages/workspace-g
308+
packages/workspace-c/packages/workspace-d/packages/workspace-e
309309
Done
310310
",
311311
}

packages/acceptance-tests/pkg-tests-specs/sources/commands/workspaces/foreach.test.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,28 @@ describe(`Commands`, () => {
110110
),
111111
);
112112

113+
test(
114+
`should support self referencing workspaces field`,
115+
makeTemporaryEnv(
116+
{
117+
private: true,
118+
workspaces: [`.`],
119+
},
120+
async ({path, run}) => {
121+
await writeFile(`${path}/.yarnrc.yml`, `plugins:\n - ${JSON.stringify(require.resolve(`@yarnpkg/monorepo/scripts/plugin-workspace-tools.js`))}\n`);
122+
await run(`install`);
123+
124+
await expect(run(`workspaces`, `foreach`, `exec`, `echo`, `42`)).resolves.toMatchObject(
125+
{
126+
code: 0,
127+
stdout: `42\nDone\n`,
128+
stderr: ``,
129+
},
130+
);
131+
},
132+
),
133+
);
134+
113135
test(
114136
`should execute 'node' command`,
115137
makeTemporaryEnv(

packages/yarnpkg-core/sources/Workspace.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -202,17 +202,21 @@ export class Workspace {
202202
* @returns all the child workspaces
203203
*/
204204
getRecursiveWorkspaceChildren() {
205-
const workspaceList: Array<Workspace> = [];
205+
const workspaceSet = new Set<Workspace>([this]);
206206

207-
for (const childWorkspaceCwd of this.workspacesCwds) {
208-
const childWorkspace = this.project.workspacesByCwd.get(childWorkspaceCwd);
207+
for (const workspace of workspaceSet) {
208+
for (const childWorkspaceCwd of workspace.workspacesCwds) {
209+
const childWorkspace = this.project.workspacesByCwd.get(childWorkspaceCwd);
209210

210-
if (childWorkspace) {
211-
workspaceList.push(childWorkspace, ...childWorkspace.getRecursiveWorkspaceChildren());
211+
if (childWorkspace) {
212+
workspaceSet.add(childWorkspace);
213+
}
212214
}
213215
}
214216

215-
return workspaceList;
217+
workspaceSet.delete(this);
218+
219+
return Array.from(workspaceSet);
216220
}
217221

218222
async persistManifest() {

0 commit comments

Comments
 (0)