diff --git a/.yarn/versions/0d12a741.yml b/.yarn/versions/0d12a741.yml new file mode 100644 index 000000000000..2d6a48f178d8 --- /dev/null +++ b/.yarn/versions/0d12a741.yml @@ -0,0 +1,34 @@ +releases: + "@yarnpkg/cli": patch + "@yarnpkg/core": patch + +declined: + - "@yarnpkg/plugin-compat" + - "@yarnpkg/plugin-constraints" + - "@yarnpkg/plugin-dlx" + - "@yarnpkg/plugin-essentials" + - "@yarnpkg/plugin-exec" + - "@yarnpkg/plugin-file" + - "@yarnpkg/plugin-git" + - "@yarnpkg/plugin-github" + - "@yarnpkg/plugin-http" + - "@yarnpkg/plugin-init" + - "@yarnpkg/plugin-interactive-tools" + - "@yarnpkg/plugin-link" + - "@yarnpkg/plugin-nm" + - "@yarnpkg/plugin-npm" + - "@yarnpkg/plugin-npm-cli" + - "@yarnpkg/plugin-pack" + - "@yarnpkg/plugin-patch" + - "@yarnpkg/plugin-pnp" + - "@yarnpkg/plugin-pnpm" + - "@yarnpkg/plugin-stage" + - "@yarnpkg/plugin-typescript" + - "@yarnpkg/plugin-version" + - "@yarnpkg/plugin-workspace-tools" + - "@yarnpkg/builder" + - "@yarnpkg/doctor" + - "@yarnpkg/extensions" + - "@yarnpkg/nm" + - "@yarnpkg/pnpify" + - "@yarnpkg/sdks" diff --git a/packages/acceptance-tests/pkg-tests-specs/sources/dragon.test.js b/packages/acceptance-tests/pkg-tests-specs/sources/dragon.test.js index c749b8ce4a60..beb74f281ef0 100644 --- a/packages/acceptance-tests/pkg-tests-specs/sources/dragon.test.js +++ b/packages/acceptance-tests/pkg-tests-specs/sources/dragon.test.js @@ -682,4 +682,48 @@ describe(`Dragon tests`, () => { }, ), ); + + test( + `it should pass the dragon test 13`, + makeTemporaryEnv( + { + workspaces: [ + `pkg-a`, + `pkg-b`, + ], + }, + async ({path, run, source}) => { + // This dragon test represents the following scenario: + // + // . + // ├── pkg-a/ + // │ └── no-deps-failing (optional) + // └── pkg-b/ + // └── no-deps-failing (not optional) + // + // Depending on the order of traversal we may end up marking no-deps-failing + // as being traversed, and skip all future traversals. If we're not being + // careful this may cause setting the "not optional" flag to be skipped as + // well, making Yarn believe that no-deps-failing is optional when it's not. + + await xfs.mkdirPromise(`${path}/pkg-a`); + await xfs.writeJsonPromise(`${path}/pkg-a/package.json`, { + name: `pkg-a`, + optionalDependencies: { + [`no-deps-failing`]: `1.0.0`, + }, + }); + + await xfs.mkdirPromise(`${path}/pkg-b`); + await xfs.writeJsonPromise(`${path}/pkg-b/package.json`, { + name: `pkg-b`, + dependencies: { + [`no-deps-failing`]: `1.0.0`, + }, + }); + + await expect(run(`install`)).rejects.toThrowError(`no-deps-failing@npm:1.0.0 couldn't be built successfully`); + }, + ), + ); }); diff --git a/packages/yarnpkg-core/sources/Project.ts b/packages/yarnpkg-core/sources/Project.ts index 57b6fa95ad54..2e07b8690766 100644 --- a/packages/yarnpkg-core/sources/Project.ts +++ b/packages/yarnpkg-core/sources/Project.ts @@ -2235,14 +2235,14 @@ function applyVirtualResolutionMutations({ }; const resolvePeerDependenciesImpl = (parentDescriptor: Descriptor, parentLocator: Locator, peerSlots: Map, {top, optional}: {top: LocatorHash, optional: boolean}) => { + if (!optional) + optionalBuilds.delete(parentLocator.locatorHash); + if (accessibleLocators.has(parentLocator.locatorHash)) return; accessibleLocators.add(parentLocator.locatorHash); - if (!optional) - optionalBuilds.delete(parentLocator.locatorHash); - const parentPackage = allPackages.get(parentLocator.locatorHash); if (!parentPackage) throw new Error(`Assertion failed: The package (${structUtils.prettyLocator(project.configuration, parentLocator)}) should have been registered`);