import fs from 'fs' import path from 'path' import { type PnpmError } from '@pnpm/error' import { store } from '@pnpm/plugin-commands-store' import { prepare } from '@pnpm/prepare' import { REGISTRY_MOCK_PORT } from '@pnpm/registry-mock' import { sync as rimraf } from '@zkochan/rimraf' import execa from 'execa' import { temporaryDirectory } from 'tempy' const REGISTRY = `http://localhost:${REGISTRY_MOCK_PORT}/` const pnpmBin = path.join(import.meta.dirname, '../../../pnpm/bin/pnpm.mjs') test('CLI fails when store status finds modified packages', async () => { const project = prepare() const tmp = temporaryDirectory() const cacheDir = path.join(tmp, 'cache') const storeDir = path.join(tmp, 'store') await execa('node', [ pnpmBin, 'add', 'is-positive@3.1.0', `--store-dir=${storeDir}`, `--registry=${REGISTRY}`, '--verify-store-integrity', ]) rimraf('node_modules/.pnpm/is-positive@3.1.0/node_modules/is-positive/index.js') let err!: PnpmError & { modified: string[] } const modulesState = project.readModulesManifest() try { await store.handler({ cacheDir, dir: process.cwd(), pnpmHomeDir: '', rawConfig: { registry: REGISTRY, }, registries: modulesState!.registries!, storeDir, userConfig: {}, dlxCacheMaxAge: 0, virtualStoreDirMaxLength: process.platform === 'win32' ? 60 : 120, }, ['status']) } catch (_err: any) { // eslint-disable-line err = _err } expect(err.code).toBe('ERR_PNPM_MODIFIED_DEPENDENCY') expect(err.modified).toHaveLength(1) expect(err.modified[0]).toMatch(/is-positive/) }) test('CLI does not fail when store status does not find modified packages', async () => { const project = prepare() fs.writeFileSync('pnpm-workspace.yaml', 'allowBuilds: { "es5-ext": false, "fsevents": true }', 'utf8') const tmp = temporaryDirectory() const cacheDir = path.join(tmp, 'cache') const storeDir = path.join(tmp, 'store') await execa('node', [ pnpmBin, `--store-dir=${storeDir}`, `--registry=${REGISTRY}`, '--verify-store-integrity', 'add', 'eslint@3.4.0', 'gulp@4.0.2', 'highcharts@5.0.10', 'is-positive@3.1.0', 'react@15.4.1', 'webpack@5.24.2', 'koorchik/node-mole-rpc', ]) // store status does not fail on not installed optional dependencies await execa('node', [ pnpmBin, 'add', 'not-compatible-with-any-os', '--save-optional', `--store-dir=${storeDir}`, `--registry=${REGISTRY}`, '--verify-store-integrity', ]) const modulesState = project.readModulesManifest() await store.handler({ cacheDir, dir: process.cwd(), pnpmHomeDir: '', rawConfig: { registry: REGISTRY, }, registries: modulesState!.registries!, storeDir, userConfig: {}, dlxCacheMaxAge: 0, virtualStoreDirMaxLength: process.platform === 'win32' ? 60 : 120, }, ['status']) }) test('CLI does not fail when storeDir is relative', async () => { const project = prepare() const cacheDir = path.resolve('cache') const workspaceDir = process.cwd() const subpackageDir = path.join(workspaceDir, 'packages', 'foo') const relativeStoreDir = '../.store' fs.writeFileSync('pnpm-workspace.yaml', `packages: - "packages/*" storeDir: "${relativeStoreDir}" `, 'utf8') // make subpackage fs.mkdirSync(subpackageDir, { recursive: true }) fs.writeFileSync( path.join(subpackageDir, 'package.json'), JSON.stringify({ name: 'foo', version: '0.0.0' }) ) await execa('node', [ pnpmBin, 'add', 'is-positive@3.1.0', `--registry=${REGISTRY}`, '--verify-store-integrity', ]) const modulesState = project.readModulesManifest() // relativeStoreDir should resolve from workspaceDir, not dir await store.handler({ cacheDir, dir: subpackageDir, workspaceDir, pnpmHomeDir: '', rawConfig: { registry: REGISTRY, }, registries: modulesState!.registries!, storeDir: relativeStoreDir, userConfig: {}, dlxCacheMaxAge: 0, virtualStoreDirMaxLength: process.platform === 'win32' ? 60 : 120, }, ['status']) })