diff --git a/.changeset/eleven-kids-chew.md b/.changeset/eleven-kids-chew.md new file mode 100644 index 00000000000..d72d4da6cc3 --- /dev/null +++ b/.changeset/eleven-kids-chew.md @@ -0,0 +1,12 @@ +--- +"@pnpm/store-path": major +"pnpm": major +--- + +Changed the location of the global store from `~/.pnpm-store` to `/store` + +On Linux, by default it will be `~/.local/share/pnpm/store` +On Windows: `%LOCALAPPDATA%/pnpm/store` +On macOS: `~/Library/pnpm/store` + +Related issue: [#2574](https://github.com/pnpm/pnpm/issues/2574) diff --git a/packages/core/test/utils/testDefaults.ts b/packages/core/test/utils/testDefaults.ts index 84c687d412f..fa0dc9c924d 100644 --- a/packages/core/test/utils/testDefaults.ts +++ b/packages/core/test/utils/testDefaults.ts @@ -45,7 +45,11 @@ export default async function testDefaults ( ...fetchOpts, }) let storeDir = opts?.storeDir ?? path.resolve('.store') - storeDir = await storePath(opts?.prefix ?? process.cwd(), storeDir) + storeDir = await storePath({ + pkgRoot: opts?.prefix ?? process.cwd(), + storePath: storeDir, + pnpmHomeDir: '', + }) const storeController = await createStore( resolve, fetchers, diff --git a/packages/headless/test/utils/testDefaults.ts b/packages/headless/test/utils/testDefaults.ts index 70b9a6e2f60..584bd3c8a62 100644 --- a/packages/headless/test/utils/testDefaults.ts +++ b/packages/headless/test/utils/testDefaults.ts @@ -35,7 +35,11 @@ export default async function testDefaults ( ], { lockfileDir } ) - storeDir = await storePath(lockfileDir, storeDir) + storeDir = await storePath({ + pkgRoot: lockfileDir, + storePath: storeDir, + pnpmHomeDir: '', + }) const authConfig = { registry } const { resolve, fetchers } = createClient({ authConfig, diff --git a/packages/mount-modules/package.json b/packages/mount-modules/package.json index d7496016157..5beb9cc5d07 100644 --- a/packages/mount-modules/package.json +++ b/packages/mount-modules/package.json @@ -44,6 +44,7 @@ }, "dependencies": { "@pnpm/cafs": "workspace:4.0.0", + "@pnpm/config": "workspace:14.0.0", "@pnpm/lockfile-file": "workspace:5.0.0", "@pnpm/lockfile-utils": "workspace:4.0.0", "@pnpm/store-path": "workspace:5.0.0", diff --git a/packages/mount-modules/src/cli.ts b/packages/mount-modules/src/cli.ts index bcdd720d5ca..e71de5df928 100644 --- a/packages/mount-modules/src/cli.ts +++ b/packages/mount-modules/src/cli.ts @@ -1,3 +1,4 @@ +import getConfig from '@pnpm/config' import { promises as fs } from 'fs' import path from 'path' @@ -7,7 +8,15 @@ import createFuseHandlers from './createFuseHandlers' (async () => { /* eslint-disable-line */ const mnt = path.join(process.cwd(), 'node_modules') await fs.mkdir(mnt, { recursive: true }) - const cafsDir = path.join(await getStorePath(process.cwd()), 'files') + const { config } = await getConfig({ + cliOptions: {}, + packageManager: { name: '', version: '' }, + }) + const cafsDir = path.join(await getStorePath({ + pkgRoot: process.cwd(), + storePath: config.storeDir, + pnpmHomeDir: config.pnpmHomeDir, + }), 'files') const fuse = new Fuse(mnt, await createFuseHandlers(process.cwd(), cafsDir), { debug: true }) fuse.mount(function (err?: Error) { if (err != null) console.error(err) diff --git a/packages/mount-modules/tsconfig.json b/packages/mount-modules/tsconfig.json index e34ce36ca16..60b46267c95 100644 --- a/packages/mount-modules/tsconfig.json +++ b/packages/mount-modules/tsconfig.json @@ -12,6 +12,9 @@ { "path": "../cafs" }, + { + "path": "../config" + }, { "path": "../dependency-path" }, diff --git a/packages/plugin-commands-env/src/node.ts b/packages/plugin-commands-env/src/node.ts index a00425f5b20..c8657fca207 100644 --- a/packages/plugin-commands-env/src/node.ts +++ b/packages/plugin-commands-env/src/node.ts @@ -83,7 +83,11 @@ async function installNode (fetch: FetchFromRegistry, wantedNodeVersion: string, }, timeout: opts.fetchTimeout, }) - const storeDir = await storePath(process.cwd(), opts.storeDir) + const storeDir = await storePath({ + pkgRoot: process.cwd(), + storePath: opts.storeDir, + pnpmHomeDir: opts.pnpmHomeDir, + }) const cafsDir = path.join(storeDir, 'files') const cafs = createCafsStore(cafsDir) const { filesIndex } = await fetchTarball(cafs, { tarball }, { diff --git a/packages/plugin-commands-installation/test/utils/index.ts b/packages/plugin-commands-installation/test/utils/index.ts index 8e0460ad55e..3a2df07346e 100644 --- a/packages/plugin-commands-installation/test/utils/index.ts +++ b/packages/plugin-commands-installation/test/utils/index.ts @@ -33,6 +33,7 @@ export const DEFAULT_OPTS = { offline: false, pending: false, pnpmfile: './.pnpmfile.cjs', + pnpmHomeDir: '', proxy: undefined, rawConfig: { registry: REGISTRY }, rawLocalConfig: {}, diff --git a/packages/plugin-commands-listing/test/utils/index.ts b/packages/plugin-commands-listing/test/utils/index.ts index 784273c3d32..3621a14f826 100644 --- a/packages/plugin-commands-listing/test/utils/index.ts +++ b/packages/plugin-commands-listing/test/utils/index.ts @@ -32,6 +32,7 @@ export const DEFAULT_OPTS = { offline: false, pending: false, pnpmfile: './.pnpmfile.cjs', + pnpmHomeDir: '', proxy: undefined, rawConfig: { registry: REGISTRY }, rawLocalConfig: {}, diff --git a/packages/plugin-commands-outdated/test/utils/index.ts b/packages/plugin-commands-outdated/test/utils/index.ts index 8a11ab0c7ac..0aa289f0896 100644 --- a/packages/plugin-commands-outdated/test/utils/index.ts +++ b/packages/plugin-commands-outdated/test/utils/index.ts @@ -34,6 +34,7 @@ export const DEFAULT_OPTS = { offline: false, pending: false, pnpmfile: './.pnpmfile.cjs', + pnpmHomeDir: '', proxy: undefined, rawConfig: { registry: REGISTRY }, rawLocalConfig: {}, diff --git a/packages/plugin-commands-publishing/test/utils/index.ts b/packages/plugin-commands-publishing/test/utils/index.ts index d44407fd638..8e4555035c1 100644 --- a/packages/plugin-commands-publishing/test/utils/index.ts +++ b/packages/plugin-commands-publishing/test/utils/index.ts @@ -32,6 +32,7 @@ export const DEFAULT_OPTS = { offline: false, pending: false, pnpmfile: './.pnpmfile.cjs', + pnpmHomeDir: '', proxy: undefined, rawConfig: { registry: REGISTRY }, rawLocalConfig: {}, diff --git a/packages/plugin-commands-rebuild/src/implementation/extendRebuildOptions.ts b/packages/plugin-commands-rebuild/src/implementation/extendRebuildOptions.ts index b637cf394a1..13a0612e677 100644 --- a/packages/plugin-commands-rebuild/src/implementation/extendRebuildOptions.ts +++ b/packages/plugin-commands-rebuild/src/implementation/extendRebuildOptions.ts @@ -22,6 +22,7 @@ export interface StrictRebuildOptions { useLockfile: boolean registries: Registries dir: string + pnpmHomeDir: string reporter: (logObj: LogBase) => void production: boolean diff --git a/packages/plugin-commands-rebuild/test/utils/index.ts b/packages/plugin-commands-rebuild/test/utils/index.ts index 91ceba6b33d..20dc53d9b8d 100644 --- a/packages/plugin-commands-rebuild/test/utils/index.ts +++ b/packages/plugin-commands-rebuild/test/utils/index.ts @@ -32,6 +32,7 @@ export const DEFAULT_OPTS = { offline: false, pending: false, pnpmfile: './.pnpmfile.cjs', + pnpmHomeDir: '', proxy: undefined, rawConfig: { registry: REGISTRY }, rawLocalConfig: {}, diff --git a/packages/plugin-commands-script-runners/test/utils/index.ts b/packages/plugin-commands-script-runners/test/utils/index.ts index b7ef78e719d..60339ab81e5 100644 --- a/packages/plugin-commands-script-runners/test/utils/index.ts +++ b/packages/plugin-commands-script-runners/test/utils/index.ts @@ -31,6 +31,7 @@ export const DEFAULT_OPTS = { offline: false, pending: false, pnpmfile: './.pnpmfile.cjs', + pnpmHomeDir: '', proxy: undefined, rawConfig: { registry: REGISTRY }, rawLocalConfig: {}, diff --git a/packages/plugin-commands-server/src/start.ts b/packages/plugin-commands-server/src/start.ts index 260cbf9be57..198d53a98e0 100644 --- a/packages/plugin-commands-server/src/start.ts +++ b/packages/plugin-commands-server/src/start.ts @@ -43,7 +43,11 @@ export default async ( if (opts.background && !Diable.isDaemon()) { Diable() } - const storeDir = await storePath(opts.dir, opts.storeDir) + const storeDir = await storePath({ + pkgRoot: opts.dir, + storePath: opts.storeDir, + pnpmHomeDir: opts.pnpmHomeDir, + }) const connectionInfoDir = serverConnectionInfoDir(storeDir) const serverJsonPath = path.join(connectionInfoDir, 'server.json') await fs.mkdir(connectionInfoDir, { recursive: true }) diff --git a/packages/plugin-commands-server/src/status.ts b/packages/plugin-commands-server/src/status.ts index 3b7079cfd5c..a8ba5202e5b 100644 --- a/packages/plugin-commands-server/src/status.ts +++ b/packages/plugin-commands-server/src/status.ts @@ -5,9 +5,13 @@ import { serverConnectionInfoDir, tryLoadServerJson } from '@pnpm/store-connecti import storePath from '@pnpm/store-path' export default async ( - opts: Pick + opts: Pick ) => { - const storeDir = await storePath(opts.dir, opts.storeDir) + const storeDir = await storePath({ + pkgRoot: opts.dir, + storePath: opts.storeDir, + pnpmHomeDir: opts.pnpmHomeDir, + }) const connectionInfoDir = serverConnectionInfoDir(storeDir) const serverJson = await tryLoadServerJson({ serverJsonPath: path.join(connectionInfoDir, 'server.json'), diff --git a/packages/plugin-commands-server/src/stop.ts b/packages/plugin-commands-server/src/stop.ts index 1fe61e459a4..e48ec42b51b 100644 --- a/packages/plugin-commands-server/src/stop.ts +++ b/packages/plugin-commands-server/src/stop.ts @@ -14,9 +14,14 @@ export default async ( opts: { storeDir?: string dir: string + pnpmHomeDir: string } ) => { - const storeDir = await storePath(opts.dir, opts.storeDir) + const storeDir = await storePath({ + pkgRoot: opts.dir, + storePath: opts.storeDir, + pnpmHomeDir: opts.pnpmHomeDir, + }) const connectionInfoDir = serverConnectionInfoDir(storeDir) const serverJson = await tryLoadServerJson({ serverJsonPath: path.join(connectionInfoDir, 'server.json'), diff --git a/packages/plugin-commands-store/src/store.ts b/packages/plugin-commands-store/src/store.ts index 4f95e1bffe4..a3a74cb7996 100644 --- a/packages/plugin-commands-store/src/store.ts +++ b/packages/plugin-commands-store/src/store.ts @@ -77,7 +77,11 @@ export async function handler (opts: StoreCommandOptions, params: string[]) { case 'status': return statusCmd(opts) case 'path': - return storePath(opts.dir, opts.storeDir) + return storePath({ + pkgRoot: opts.dir, + storePath: opts.storeDir, + pnpmHomeDir: opts.pnpmHomeDir, + }) case 'prune': { store = await createOrConnectStoreController(opts) const storePruneOptions = Object.assign(opts, { @@ -102,7 +106,11 @@ export async function handler (opts: StoreCommandOptions, params: string[]) { async function statusCmd (opts: StoreCommandOptions) { const modifiedPkgs = await storeStatus(Object.assign(opts, { - storeDir: await storePath(opts.dir, opts.storeDir), + storeDir: await storePath({ + pkgRoot: opts.dir, + storePath: opts.storeDir, + pnpmHomeDir: opts.pnpmHomeDir, + }), })) if (!modifiedPkgs || (modifiedPkgs.length === 0)) { logger.info({ diff --git a/packages/store-connection-manager/src/index.ts b/packages/store-connection-manager/src/index.ts index 9ff180f869d..fce1cbcf8ec 100644 --- a/packages/store-connection-manager/src/index.ts +++ b/packages/store-connection-manager/src/index.ts @@ -17,6 +17,7 @@ export { createNewStoreController, serverConnectionInfoDir } export type CreateStoreControllerOptions = Omit & Pick>, opts: CreateStoreControllerOptions ) { - const storeDir = await storePath(opts.dir, opts.storeDir) + const storeDir = await storePath({ + pkgRoot: opts.dir, + storePath: opts.storeDir, + pnpmHomeDir: opts.pnpmHomeDir, + }) if (!storeControllerCache.has(storeDir)) { storeControllerCache.set(storeDir, createOrConnectStoreController(opts)) } @@ -39,7 +44,11 @@ export async function createOrConnectStoreController ( ctrl: StoreController dir: string }> { - const storeDir = await storePath(opts.workspaceDir ?? opts.dir, opts.storeDir) + const storeDir = await storePath({ + pkgRoot: opts.workspaceDir ?? opts.dir, + storePath: opts.storeDir, + pnpmHomeDir: opts.pnpmHomeDir, + }) const connectionInfoDir = serverConnectionInfoDir(storeDir) const serverJsonPath = path.join(connectionInfoDir, 'server.json') let serverJson = await tryLoadServerJson({ serverJsonPath, shouldRetryOnNoent: false }) diff --git a/packages/store-path/src/index.ts b/packages/store-path/src/index.ts index 7e7ffe49b13..43613f09baa 100644 --- a/packages/store-path/src/index.ts +++ b/packages/store-path/src/index.ts @@ -11,12 +11,23 @@ import touch from 'touch' const STORE_VERSION = 'v3' export default function ( - pkgRoot: string, - storePath?: string + { + pkgRoot, + storePath, + pnpmHomeDir, + }: { + pkgRoot: string + storePath?: string + pnpmHomeDir: string + } ) { - if (!storePath || isHomepath(storePath)) { - const relStorePath = storePath ? storePath.substring(2) : '.pnpm-store' - return storePathRelativeToHome(pkgRoot, relStorePath) + if (!storePath) { + return storePathRelativeToHome(pkgRoot, 'store', pnpmHomeDir) + } + + if (isHomepath(storePath)) { + const homedir = getHomedir() + return storePathRelativeToHome(pkgRoot, storePath.substring(2), homedir) } const storeBasePath = pathAbsolute(storePath, pkgRoot) @@ -27,16 +38,16 @@ export default function ( return path.join(storeBasePath, STORE_VERSION) } -async function storePathRelativeToHome (pkgRoot: string, relStore: string) { +async function storePathRelativeToHome (pkgRoot: string, relStore: string, homedir: string) { const tempFile = pathTemp(pkgRoot) await fs.mkdir(path.dirname(tempFile), { recursive: true }) await touch(tempFile) - const homedir = getHomedir() + const storeInHomeDir = path.join(homedir, relStore, STORE_VERSION) if (await canLinkToSubdir(tempFile, homedir)) { await fs.unlink(tempFile) // If the project is on the drive on which the OS home directory // then the store is placed in the home directory - return path.join(homedir, relStore, STORE_VERSION) + return storeInHomeDir } try { let mountpoint = await rootLinkTarget(tempFile) @@ -50,13 +61,13 @@ async function storePathRelativeToHome (pkgRoot: string, relStore: string) { // If linking works only in the project folder // then prefer to place the store inside the homedir if (dirsAreEqual(pkgRoot, mountpoint)) { - return path.join(homedir, relStore, STORE_VERSION) + return storeInHomeDir } - return path.join(mountpoint, relStore, STORE_VERSION) + return path.join(mountpoint, '.pnpm-store', STORE_VERSION) } catch (err) { // this is an unlikely situation but if there is no way to find // a linkable place on the disk, create the store in homedir - return path.join(homedir, relStore, STORE_VERSION) + return storeInHomeDir } finally { await fs.unlink(tempFile) } diff --git a/packages/store-path/test/index.ts b/packages/store-path/test/index.ts index 73615e23c09..550fdb00301 100644 --- a/packages/store-path/test/index.ts +++ b/packages/store-path/test/index.ts @@ -7,13 +7,22 @@ jest.mock('fs') const skipOnWindows = isWindows() ? test.skip : test skipOnWindows('when a link can be created to the homedir', async () => { - expect(await storePath('/can-link-to-homedir')).toBe('/home/user/.pnpm-store/v3') + expect(await storePath({ + pkgRoot: '/can-link-to-homedir', + pnpmHomeDir: '/local/share/pnpm', + })).toBe('/local/share/pnpm/store/v3') }) skipOnWindows('a link can be created to the root of the drive', async () => { - expect(await storePath('/src/workspace/project')).toBe('/.pnpm-store/v3') + expect(await storePath({ + pkgRoot: '/src/workspace/project', + pnpmHomeDir: '/local/share/pnpm', + })).toBe('/.pnpm-store/v3') }) skipOnWindows('a link can be created to the a subdir in the root of the drive', async () => { - expect(await storePath('/mnt/project')).toBe('/mnt/.pnpm-store/v3') + expect(await storePath({ + pkgRoot: '/mnt/project', + pnpmHomeDir: '/local/share/pnpm', + })).toBe('/mnt/.pnpm-store/v3') }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 101c30ee9a4..bb5b35a4c72 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1600,6 +1600,7 @@ importers: packages/mount-modules: specifiers: '@pnpm/cafs': workspace:4.0.0 + '@pnpm/config': workspace:14.0.0 '@pnpm/lockfile-file': workspace:5.0.0 '@pnpm/lockfile-utils': workspace:4.0.0 '@pnpm/logger': ^4.0.0 @@ -1615,6 +1616,7 @@ importers: rimraf: ^3.0.2 dependencies: '@pnpm/cafs': link:../cafs + '@pnpm/config': link:../config '@pnpm/lockfile-file': link:../lockfile-file '@pnpm/lockfile-utils': link:../lockfile-utils '@pnpm/store-path': link:../store-path