feat: the global store location should be inside the pnpm home directory (#4522)

This commit is contained in:
Zoltan Kochan
2022-04-02 23:34:46 +03:00
committed by GitHub
parent 1e0d20ea2d
commit cdeb652035
22 changed files with 122 additions and 26 deletions

View File

@@ -0,0 +1,12 @@
---
"@pnpm/store-path": major
"pnpm": major
---
Changed the location of the global store from `~/.pnpm-store` to `<pnpm home directory>/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)

View File

@@ -45,7 +45,11 @@ export default async function testDefaults<T> (
...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,

View File

@@ -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,

View File

@@ -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",

View File

@@ -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)

View File

@@ -12,6 +12,9 @@
{
"path": "../cafs"
},
{
"path": "../config"
},
{
"path": "../dependency-path"
},

View File

@@ -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 }, {

View File

@@ -33,6 +33,7 @@ export const DEFAULT_OPTS = {
offline: false,
pending: false,
pnpmfile: './.pnpmfile.cjs',
pnpmHomeDir: '',
proxy: undefined,
rawConfig: { registry: REGISTRY },
rawLocalConfig: {},

View File

@@ -32,6 +32,7 @@ export const DEFAULT_OPTS = {
offline: false,
pending: false,
pnpmfile: './.pnpmfile.cjs',
pnpmHomeDir: '',
proxy: undefined,
rawConfig: { registry: REGISTRY },
rawLocalConfig: {},

View File

@@ -34,6 +34,7 @@ export const DEFAULT_OPTS = {
offline: false,
pending: false,
pnpmfile: './.pnpmfile.cjs',
pnpmHomeDir: '',
proxy: undefined,
rawConfig: { registry: REGISTRY },
rawLocalConfig: {},

View File

@@ -32,6 +32,7 @@ export const DEFAULT_OPTS = {
offline: false,
pending: false,
pnpmfile: './.pnpmfile.cjs',
pnpmHomeDir: '',
proxy: undefined,
rawConfig: { registry: REGISTRY },
rawLocalConfig: {},

View File

@@ -22,6 +22,7 @@ export interface StrictRebuildOptions {
useLockfile: boolean
registries: Registries
dir: string
pnpmHomeDir: string
reporter: (logObj: LogBase) => void
production: boolean

View File

@@ -32,6 +32,7 @@ export const DEFAULT_OPTS = {
offline: false,
pending: false,
pnpmfile: './.pnpmfile.cjs',
pnpmHomeDir: '',
proxy: undefined,
rawConfig: { registry: REGISTRY },
rawLocalConfig: {},

View File

@@ -31,6 +31,7 @@ export const DEFAULT_OPTS = {
offline: false,
pending: false,
pnpmfile: './.pnpmfile.cjs',
pnpmHomeDir: '',
proxy: undefined,
rawConfig: { registry: REGISTRY },
rawLocalConfig: {},

View File

@@ -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 })

View File

@@ -5,9 +5,13 @@ import { serverConnectionInfoDir, tryLoadServerJson } from '@pnpm/store-connecti
import storePath from '@pnpm/store-path'
export default async (
opts: Pick<Config, 'dir' | 'storeDir'>
opts: Pick<Config, 'dir' | 'pnpmHomeDir' | 'storeDir'>
) => {
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'),

View File

@@ -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'),

View File

@@ -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({

View File

@@ -17,6 +17,7 @@ export { createNewStoreController, serverConnectionInfoDir }
export type CreateStoreControllerOptions = Omit<CreateNewStoreControllerOptions, 'storeDir'> & Pick<Config,
| 'storeDir'
| 'dir'
| 'pnpmHomeDir'
| 'useRunningStoreServer'
| 'useStoreServer'
| 'workspaceDir'
@@ -26,7 +27,11 @@ export async function createOrConnectStoreControllerCached (
storeControllerCache: Map<string, Promise<{ctrl: StoreController, dir: string}>>,
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 })

View File

@@ -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)
}

View File

@@ -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')
})

2
pnpm-lock.yaml generated
View File

@@ -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