diff --git a/.yarn/versions/5d3e3f4c.yml b/.yarn/versions/5d3e3f4c.yml new file mode 100644 index 000000000000..b9ef967a4a38 --- /dev/null +++ b/.yarn/versions/5d3e3f4c.yml @@ -0,0 +1,23 @@ +releases: + "@yarnpkg/cli": patch + "@yarnpkg/plugin-essentials": patch + +declined: + - "@yarnpkg/plugin-compat" + - "@yarnpkg/plugin-constraints" + - "@yarnpkg/plugin-dlx" + - "@yarnpkg/plugin-init" + - "@yarnpkg/plugin-interactive-tools" + - "@yarnpkg/plugin-nm" + - "@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/core" + - "@yarnpkg/doctor" diff --git a/packages/acceptance-tests/pkg-tests-specs/sources/commands/info.test.ts b/packages/acceptance-tests/pkg-tests-specs/sources/commands/info.test.ts index 213430293918..fc3d3c92eb59 100644 --- a/packages/acceptance-tests/pkg-tests-specs/sources/commands/info.test.ts +++ b/packages/acceptance-tests/pkg-tests-specs/sources/commands/info.test.ts @@ -61,6 +61,26 @@ describe(`Commands`, () => { }), ); + test( + `it should report virtual locators for nested virtual dependencies`, + makeTemporaryEnv({ + dependencies: { + [`peer-deps-lvl0`]: `1.0.0`, + }, + }, async ({path, run, source}) => { + await run(`install`); + + const {stdout} = await run(`info`, `peer-deps-lvl1`, `--recursive`, `--virtuals`, `--json`); + const data = stdout.match(/.*\n/g)!.map(line => JSON.parse(line)); + const base = data.find(entry => entry.value === `peer-deps-lvl1@npm:1.0.0`); + + expect(base.children.Dependencies).toEqual([{ + descriptor: `peer-deps-lvl2@npm:1.0.0`, + locator: expect.stringMatching(/^peer-deps-lvl2@virtual:/), + }]); + }), + ); + test( `it shouldn't print info for other workspaces by default`, makeTemporaryEnv({ diff --git a/packages/plugin-essentials/sources/commands/info.ts b/packages/plugin-essentials/sources/commands/info.ts index 3f728d60ed3f..b405885c24ae 100644 --- a/packages/plugin-essentials/sources/commands/info.ts +++ b/packages/plugin-essentials/sources/commands/info.ts @@ -1,10 +1,10 @@ -import {BaseCommand, WorkspaceRequiredError} from '@yarnpkg/cli'; -import {Configuration, Project, structUtils, Workspace, LocatorHash, Package, formatUtils, miscUtils, Locator, Cache, FetchOptions, ThrowReport, Manifest, treeUtils} from '@yarnpkg/core'; -import {xfs} from '@yarnpkg/fslib'; -import {Command, Option, Usage, UsageError} from 'clipanion'; -import mm from 'micromatch'; +import {BaseCommand, WorkspaceRequiredError} from '@yarnpkg/cli'; +import {Configuration, Project, structUtils, Workspace, LocatorHash, Package, formatUtils, miscUtils, Locator, Cache, FetchOptions, ThrowReport, Manifest, treeUtils, IdentHash, Descriptor} from '@yarnpkg/core'; +import {xfs} from '@yarnpkg/fslib'; +import {Command, Option, Usage, UsageError} from 'clipanion'; +import mm from 'micromatch'; -import {Hooks} from '..'; +import {Hooks} from '..'; // eslint-disable-next-line arca/no-default-export export default class InfoCommand extends BaseCommand { @@ -180,9 +180,13 @@ export default class InfoCommand extends BaseCommand { }; }); - const sortedLookup = miscUtils.sortMap([...lookupSet], pkg => { - return structUtils.stringifyLocator(pkg); - }); + const sortedLookup = miscUtils.sortMap([...lookupSet], [ + pkg => structUtils.stringifyLocator(structUtils.isVirtualLocator(pkg) + ? structUtils.devirtualizeLocator(pkg) + : pkg), + pkg => structUtils.isVirtualLocator(pkg) ? `1` : `0`, + pkg => structUtils.stringifyLocator(pkg), + ]); const selection = sortedLookup.filter(pkg => { return matchers.length === 0 || matchers.some(matcher => matcher(pkg)); @@ -225,6 +229,7 @@ export default class InfoCommand extends BaseCommand { const infoTreeChildren: treeUtils.TreeMap = {}; const infoTree: treeUtils.TreeNode = {children: infoTreeChildren}; + const dependencyData = new Map>(); const fetcher = configuration.makeFetcher(); const fetcherOptions: FetchOptions = {project, fetcher, cache, checksums: project.storedChecksums, report: new ThrowReport(), cacheOptions: {skipIntegrityCheck: true}}; @@ -353,18 +358,41 @@ export default class InfoCommand extends BaseCommand { } if (pkg.dependencies.size > 0 && !isVirtual) { - registerData(`Dependencies`, [...pkg.dependencies.values()].map(dependency => { + const dependencies = new Map([...pkg.dependencies.values()].map(dependency => { const resolutionHash = project.storedResolutions.get(dependency.descriptorHash); const resolution = typeof resolutionHash !== `undefined` ? project.storedPackages.get(resolutionHash) ?? null : null; - return formatUtils.tuple(formatUtils.Type.RESOLUTION, { + return [dependency.identHash, { descriptor: dependency, locator: resolution, - }); + }] as const; })); + + dependencyData.set(pkg.locatorHash, dependencies); + registerData(`Dependencies`, [...dependencies.values()].map(dependency => { + return formatUtils.tuple(formatUtils.Type.RESOLUTION, dependency); + })); + } + + if (isVirtual) { + const base = structUtils.devirtualizeLocator(pkg); + const baseDependencies = dependencyData.get(base.locatorHash); + + if (typeof baseDependencies !== `undefined`) { + for (const dependency of pkg.dependencies.values()) { + const baseDependency = baseDependencies.get(dependency.identHash); + if (typeof baseDependency === `undefined`) + continue; + + const resolutionHash = project.storedResolutions.get(dependency.descriptorHash); + baseDependency.locator = typeof resolutionHash !== `undefined` + ? project.storedPackages.get(resolutionHash) ?? null + : null; + } + } } if (pkg.peerDependencies.size > 0 && isVirtual) {