Skip to content

Commit f5551e9

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 f5551e9

File tree

4 files changed

+66
-7
lines changed

4 files changed

+66
-7
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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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
}

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,27 @@ 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 run(`install`);
122+
123+
await expect(run(`workspaces`, `foreach`, `--worktree`, `exec`, `echo`, `42`)).resolves.toMatchObject(
124+
{
125+
code: 0,
126+
stdout: `42\nDone\n`,
127+
stderr: ``,
128+
},
129+
);
130+
},
131+
),
132+
);
133+
113134
test(
114135
`should execute 'node' command`,
115136
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)