diff --git a/.yarn/versions/81e42c15.yml b/.yarn/versions/81e42c15.yml new file mode 100644 index 000000000000..348205cb3c76 --- /dev/null +++ b/.yarn/versions/81e42c15.yml @@ -0,0 +1,23 @@ +releases: + "@yarnpkg/cli": minor + "@yarnpkg/plugin-pnpm": minor + +declined: + - "@yarnpkg/plugin-compat" + - "@yarnpkg/plugin-constraints" + - "@yarnpkg/plugin-dlx" + - "@yarnpkg/plugin-essentials" + - "@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-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/features/pnpmStoreLocation.test.ts b/packages/acceptance-tests/pkg-tests-specs/sources/features/pnpmStoreLocation.test.ts new file mode 100644 index 000000000000..d96ebc2d9b3c --- /dev/null +++ b/packages/acceptance-tests/pkg-tests-specs/sources/features/pnpmStoreLocation.test.ts @@ -0,0 +1,47 @@ +import {xfs, ppath} from '@yarnpkg/fslib'; + +const { + fs: {FsLinkType, determineLinkType}, +} = require(`pkg-tests-core`); + +const customStoreFolderName = `.customStore`; + +describe(`Features`, () => { + describe(`pnpmStoreLocation`, () => { + test( + `it should create the store at custom path and symlink all files to the custom store location`, + makeTemporaryEnv( + { + dependencies: {[`no-deps`]: `1.0.0`}, + }, + { + nodeLinker: `pnpm`, + pnpmStoreFolder: customStoreFolderName, + winLinkType: `symlinks`, + }, + async ({path, run, source}) => { + await run(`install`); + + // Ensure that the customized folder is created + const absolutePnpmStorePath = ppath.join(path, customStoreFolderName); + expect(xfs.existsSync(absolutePnpmStorePath)).toEqual(true); + + // Ensure that the default node_modules/.store folder is not created + expect(xfs.existsSync(ppath.join(path, `node_modules`, `.store`))).toEqual(false); + + // Ensure that the installed package is a symbolic link + const installedPackagePath = ppath.join(path, `node_modules`, `no-deps`); + expect(await determineLinkType(installedPackagePath)).toEqual(FsLinkType.SYMBOLIC); + + // Ensure that the link target is a relative path + const installedPackageLinkTarget = await xfs.readlinkPromise(installedPackagePath); + expect(ppath.isAbsolute(installedPackageLinkTarget)).toBeFalsy(); + + // Ensure that the resolved link target is within the customized pnpmStoreFolder. + const resolvedPackageLinkTarget = ppath.join(ppath.dirname(installedPackagePath), installedPackageLinkTarget); + expect(ppath.contains(absolutePnpmStorePath, resolvedPackageLinkTarget)).toBeTruthy(); + }, + ), + ); + }); +}); diff --git a/packages/docusaurus/static/configuration/yarnrc.json b/packages/docusaurus/static/configuration/yarnrc.json index ca3ef4fdf317..92a304c23ce6 100644 --- a/packages/docusaurus/static/configuration/yarnrc.json +++ b/packages/docusaurus/static/configuration/yarnrc.json @@ -481,6 +481,14 @@ "type": "string", "default": "pnp" }, + "pnpmStoreFolder": { + "_package": "@yarnpkg/plugin-pnpm", + "title": "Path where the pnpm store will be stored", + "description": "By default, the store is stored in the `node_modules/.store` of the project. Sometimes in CI scenario's it is convenient to store this in a different location so it can be cached and reused.", + "type": "string", + "format": "uri-reference", + "examples": [".cache/.store"] + }, "winLinkType": { "_package": "@yarnpkg/core", "title": "Define whether to use junctions or symlinks when creating links on Windows.", diff --git a/packages/plugin-pnpm/sources/PnpmLinker.ts b/packages/plugin-pnpm/sources/PnpmLinker.ts index 5627b758f5de..6a0db08dc765 100644 --- a/packages/plugin-pnpm/sources/PnpmLinker.ts +++ b/packages/plugin-pnpm/sources/PnpmLinker.ts @@ -309,7 +309,7 @@ function getNodeModulesLocation(project: Project) { } function getStoreLocation(project: Project) { - return ppath.join(getNodeModulesLocation(project), `.store`); + return project.configuration.get(`pnpmStoreFolder`); } function getPackagePaths(locator: Locator, {project}: {project: Project}) { diff --git a/packages/plugin-pnpm/sources/index.ts b/packages/plugin-pnpm/sources/index.ts index a4a33ab3affd..b8b7e2931e07 100644 --- a/packages/plugin-pnpm/sources/index.ts +++ b/packages/plugin-pnpm/sources/index.ts @@ -1,10 +1,24 @@ -import {Plugin} from '@yarnpkg/core'; +import {Plugin, SettingsType} from '@yarnpkg/core'; +import {PortablePath} from '@yarnpkg/fslib'; -import {PnpmLinker} from './PnpmLinker'; +import {PnpmLinker} from './PnpmLinker'; export {PnpmLinker}; +declare module '@yarnpkg/core' { + interface ConfigurationValueMap { + pnpmStoreFolder: PortablePath; + } +} + const plugin: Plugin = { + configuration: { + pnpmStoreFolder: { + description: `By default, the store is stored in the 'node_modules/.store' of the project. Sometimes in CI scenario's it is convenient to store this in a different location so it can be cached and reused.`, + type: SettingsType.ABSOLUTE_PATH, + default: `./node_modules/.store`, + }, + }, linkers: [ PnpmLinker, ],