diff --git a/.pnp.cjs b/.pnp.cjs index 8963d147ef16..98f17a847705 100755 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -268,6 +268,7 @@ const RAW_RUNTIME_STATE = ["@yarnpkg/types", "workspace:packages/yarnpkg-types"],\ ["chalk", "npm:3.0.0"],\ ["clipanion", "virtual:576bf3e379b293160348e4cadfbd6541796e6f78477b0875c4437065090cec6f78b6ec2281b8e15d1c870d61578dc7dee16a5ae49a65701fec83e592ce2ebdeb#npm:4.0.0-rc.2"],\ + ["cross-spawn", "npm:7.0.3"],\ ["esbuild", [\ "esbuild-wasm",\ "npm:0.23.0"\ @@ -9610,6 +9611,7 @@ const RAW_RUNTIME_STATE = ["@yarnpkg/types", "workspace:packages/yarnpkg-types"],\ ["chalk", "npm:3.0.0"],\ ["clipanion", "virtual:576bf3e379b293160348e4cadfbd6541796e6f78477b0875c4437065090cec6f78b6ec2281b8e15d1c870d61578dc7dee16a5ae49a65701fec83e592ce2ebdeb#npm:4.0.0-rc.2"],\ + ["cross-spawn", "npm:7.0.3"],\ ["esbuild", [\ "esbuild-wasm",\ "npm:0.23.0"\ diff --git a/.yarn/versions/67af515f.yml b/.yarn/versions/67af515f.yml new file mode 100644 index 000000000000..11300a475dea --- /dev/null +++ b/.yarn/versions/67af515f.yml @@ -0,0 +1,32 @@ +releases: + "@yarnpkg/builder": patch + "@yarnpkg/cli": patch + "@yarnpkg/core": patch + "@yarnpkg/doctor": patch + "@yarnpkg/extensions": patch + "@yarnpkg/nm": patch + "@yarnpkg/plugin-compat": patch + "@yarnpkg/plugin-constraints": patch + "@yarnpkg/plugin-dlx": patch + "@yarnpkg/plugin-essentials": patch + "@yarnpkg/plugin-exec": patch + "@yarnpkg/plugin-file": patch + "@yarnpkg/plugin-git": patch + "@yarnpkg/plugin-github": patch + "@yarnpkg/plugin-http": patch + "@yarnpkg/plugin-init": patch + "@yarnpkg/plugin-interactive-tools": patch + "@yarnpkg/plugin-link": patch + "@yarnpkg/plugin-nm": patch + "@yarnpkg/plugin-npm": patch + "@yarnpkg/plugin-npm-cli": patch + "@yarnpkg/plugin-pack": patch + "@yarnpkg/plugin-patch": patch + "@yarnpkg/plugin-pnp": patch + "@yarnpkg/plugin-pnpm": patch + "@yarnpkg/plugin-stage": patch + "@yarnpkg/plugin-typescript": patch + "@yarnpkg/plugin-version": patch + "@yarnpkg/plugin-workspace-tools": patch + "@yarnpkg/pnpify": patch + "@yarnpkg/sdks": patch diff --git a/package.json b/package.json index 8ae492bda1f5..6eca86a4a768 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "@iarna/toml": "^2.2.5", "@yarnpkg/types": "workspace:^", "chalk": "^3.0.0", + "cross-spawn": "^7.0.3", "micromatch": "^4.0.2", "semver": "^7.1.2" }, diff --git a/packages/plugin-essentials/sources/commands/link.ts b/packages/plugin-essentials/sources/commands/link.ts index 04aa0d20e8a1..a47b7d174dfc 100644 --- a/packages/plugin-essentials/sources/commands/link.ts +++ b/packages/plugin-essentials/sources/commands/link.ts @@ -1,8 +1,10 @@ import {BaseCommand, WorkspaceRequiredError} from '@yarnpkg/cli'; import {Cache, Configuration, Project, structUtils} from '@yarnpkg/core'; -import {npath, ppath} from '@yarnpkg/fslib'; +import {npath, ppath, constants} from '@yarnpkg/fslib'; import {Command, Option, Usage, UsageError} from 'clipanion'; +import {Report} from '../../../yarnpkg-core/sources'; + // eslint-disable-next-line arca/no-default-export export default class LinkCommand extends BaseCommand { static paths = [ @@ -50,7 +52,7 @@ export default class LinkCommand extends BaseCommand { }); const topLevelWorkspace = project.topLevelWorkspace; - const linkedWorkspaces = []; + const linkedWorkspaces: Array = []; for (const destination of this.destinations) { const absoluteDestination = ppath.resolve(this.context.cwd, npath.toPortablePath(destination)); @@ -87,20 +89,50 @@ export default class LinkCommand extends BaseCommand { } } - for (const workspace of linkedWorkspaces) { + const processWorkspace = async (workspace: any) => { const fullName = structUtils.stringifyIdent(workspace.anchoredLocator); - const target = this.relative + let target = this.relative ? ppath.relative(project.cwd, workspace.cwd) : workspace.cwd; - topLevelWorkspace.manifest.resolutions.push({ + if (process.platform === `win32`) { + const windowsPath = npath.fromPortablePath(target); + + if (windowsPath.length >= constants.MAX_PATH) { + // For virtual packages, try to shorten the path first + if (structUtils.isVirtualLocator(workspace.anchoredLocator)) { + const hash = structUtils.slugifyLocator(workspace.anchoredLocator).slice(0, 8); + const shortName = `${workspace.manifest.name.name}-${hash}`; + target = ppath.resolve(project.cwd, `node_modules/${shortName}` as any); + } + + const finalWindowsPath = npath.fromPortablePath(target); + if (finalWindowsPath.length >= constants.MAX_PATH) { + target = npath.toPortablePath(`\\\\?\\${finalWindowsPath}`); + } + } + } + + return { pattern: {descriptor: {fullName}}, reference: `portal:${target}`, - }); - } + }; + }; + + const resolutions = await Promise.all(linkedWorkspaces.map(processWorkspace)); + topLevelWorkspace.manifest.resolutions.push(...resolutions); return await project.installWithNewReport({ stdout: this.context.stdout, + reportFooter: () => { + const rows = resolutions.map(({pattern, reference}) => [ + structUtils.prettyIdent(configuration, pattern.descriptor), + reference, + ]); + + return `${Report.reportInfo(null, `The following packages have been linked:`)}\n${ + Report.reportIndex(null, rows)}`; + }, }, { cache, }); diff --git a/packages/yarnpkg-core/tests/Link.test.ts b/packages/yarnpkg-core/tests/Link.test.ts new file mode 100644 index 000000000000..65cdfbfe80a3 --- /dev/null +++ b/packages/yarnpkg-core/tests/Link.test.ts @@ -0,0 +1,45 @@ +import {structUtils} from '@yarnpkg/core'; +import {ppath, npath, PortablePath, xfs} from '@yarnpkg/fslib'; + +import {generatePath} from '../sources/commands/link'; + +describe(`Link`, () => { + describe(`generatePath`, () => { + it(`handles Windows long paths correctly`, async () => { + const originalPlatform = process.platform; + Object.defineProperty(process, `platform`, { + value: `win32`, + }); + + try { + const mockProject = { + cwd: npath.toPortablePath(`C:\\very\\long\\path\\that\\exceeds\\windows\\limits\\project`) as PortablePath, + }; + + const mockLocator = structUtils.makeLocator( + structUtils.makeIdent(`firebase`, `app-check`), + `virtual:1234567890abcdef`, + ); + + const baseFs = new xfs.JailFS(mockProject.cwd); + + const result = await generatePath(mockLocator, { + baseFs, + project: mockProject as any, + isDependency: true, + }); + + if (npath.fromPortablePath(result).length >= 260) + expect(npath.fromPortablePath(result)).toMatch(/^\\\\\?\\./); + + + await expect(baseFs.mkdirPromise(ppath.dirname(result), {recursive: true})) + .resolves.not.toThrow(); + } finally { + Object.defineProperty(process, `platform`, { + value: originalPlatform, + }); + } + }); + }); +}); diff --git a/yarn.lock b/yarn.lock index 5363fc804cbf..f27910ef5ae1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5735,6 +5735,7 @@ __metadata: "@yarnpkg/types": "workspace:^" chalk: "npm:^3.0.0" clipanion: "npm:^4.0.0-rc.2" + cross-spawn: "npm:^7.0.3" esbuild: "npm:esbuild-wasm@^0.23.0" eslint: "npm:^8.57.0" jest: "npm:^29.2.1"