mirror of
https://github.com/pnpm/pnpm.git
synced 2026-07-18 19:52:31 -04:00
Replace the external `@pnpm/registry-mock` (Verdaccio) test dependency with an in-repo, in-process registry that serves package fixtures to **both** the pacquet Rust tests and the pnpm CLI (Jest) tests. No separately managed registry process is needed. ### How it works - **Fixtures** live at `registry/.fixtures/packages/<name>/<version>/…`, moved verbatim from [`pnpm/registry-mock`](https://github.com/pnpm/registry-mock) (keyed by each `package.json`'s `name`+`version`). - **`pnpm-registry-fixtures`** builds verdaccio-shaped storage from those fixtures; the in-tree **`pnpm-registry`** crate serves it. - Files whose names differ only by case (`@pnpm.e2e/with-same-file-in-different-cases`) and `bundleDependencies` trees are composed **in memory** by the builder, since neither can be committed to the working tree. - **pacquet**: `pacquet-testing-utils`' `TestRegistry` starts the server lazily (once per process) in proxy mode, serving `@pnpm.e2e` fixtures locally and falling through to the npm uplink for real packages (`is-positive`, `is-negative`, …) — matching how registry-mock behaved. - **pnpm CLI**: the `with-registry` Jest `globalSetup` builds storage from the fixtures via the new `pnpm-registry-prepare` binary (built from source in the Test CI job) and serves it with `pnpm-registry`. `REGISTRY_MOCK_PORT` / `REGISTRY_MOCK_CREDENTIALS` / `getIntegrity` now come from `@pnpm/testing.registry-mock`. ### Result `@pnpm/registry-mock` is removed from every manifest, the catalog, and `packageExtensions`; `cargo test` / `cargo nextest run` / `just test` and the pnpm CLI Jest suites all run registry-backed tests without launching Verdaccio.
186 lines
6.7 KiB
TypeScript
186 lines
6.7 KiB
TypeScript
import fs from 'node:fs'
|
|
import { createRequire } from 'node:module'
|
|
import path from 'node:path'
|
|
import util from 'node:util'
|
|
|
|
import { expect } from '@jest/globals'
|
|
import { assertStore } from '@pnpm/assert-store'
|
|
import { WANTED_LOCKFILE } from '@pnpm/constants'
|
|
import type { Modules } from '@pnpm/installing.modules-yaml'
|
|
import type { LockfileFile } from '@pnpm/lockfile.types'
|
|
import { REGISTRY_MOCK_PORT } from '@pnpm/testing.registry-mock'
|
|
import yaml from 'js-yaml'
|
|
import { readYamlFileSync } from 'read-yaml-file'
|
|
import { writePackageSync } from 'write-package'
|
|
|
|
import isExecutable from './isExecutable.js'
|
|
|
|
const require = createRequire(import.meta.url)
|
|
|
|
export { isExecutable, type Modules }
|
|
|
|
export interface Project {
|
|
// eslint-disable-next-line
|
|
requireModule: (moduleName: string) => any
|
|
dir: () => string
|
|
has: (pkgName: string, modulesDir?: string) => void
|
|
hasNot: (pkgName: string, modulesDir?: string) => void
|
|
getStorePath: () => string
|
|
resolve: (pkgName: string, version?: string, relativePath?: string) => string
|
|
getPkgIndexFilePath: (pkgName: string, version: string) => string
|
|
cafsHas: (pkgName: string, version: string) => void
|
|
cafsHasNot: (pkgName: string, version: string) => void
|
|
storeHas: (pkgName: string, version?: string) => string
|
|
storeHasNot: (pkgName: string, version?: string) => void
|
|
isExecutable: (pathToExe: string) => void
|
|
/**
|
|
* TODO: Remove the `Required<T>` cast.
|
|
*
|
|
* https://github.com/microsoft/TypeScript/pull/32695 might help with this.
|
|
*/
|
|
readCurrentLockfile: () => Required<LockfileFile>
|
|
readModulesManifest: () => Modules | null
|
|
/**
|
|
* TODO: Remove the `Required<T>` cast.
|
|
*
|
|
* https://github.com/microsoft/TypeScript/pull/32695 might help with this.
|
|
*/
|
|
readLockfile: (lockfileName?: string) => Required<LockfileFile>
|
|
writePackageJson: (pkgJson: object) => void
|
|
}
|
|
|
|
export function assertProject (projectPath: string, encodedRegistryName?: string): Project {
|
|
const ern = encodedRegistryName ?? `localhost+${REGISTRY_MOCK_PORT}`
|
|
const modules = path.join(projectPath, 'node_modules')
|
|
|
|
interface StoreInstance {
|
|
storePath: string
|
|
getPkgIndexFilePath: (pkgName: string, version: string) => string
|
|
cafsHas: (pkgName: string, version: string) => void
|
|
cafsHasNot: (pkgName: string, version: string) => void
|
|
storeHas: (pkgName: string, version?: string) => void
|
|
storeHasNot: (pkgName: string, version?: string) => void
|
|
resolve: (pkgName: string, version?: string, relativePath?: string) => string
|
|
}
|
|
let cachedStore: StoreInstance
|
|
function getStoreInstance (): StoreInstance {
|
|
if (!cachedStore) {
|
|
const modulesYaml = readModulesManifest(modules)
|
|
if (modulesYaml == null) {
|
|
throw new Error(`Cannot find module store. No .modules.yaml found at "${modules}"`)
|
|
}
|
|
const storePath = modulesYaml.storeDir
|
|
cachedStore = {
|
|
storePath,
|
|
...assertStore(storePath, ern),
|
|
}
|
|
}
|
|
return cachedStore
|
|
}
|
|
function getVirtualStoreDir (): string {
|
|
const modulesYaml = readModulesManifest(modules)
|
|
if (modulesYaml == null) {
|
|
return path.join(modules, '.pnpm')
|
|
}
|
|
if (path.isAbsolute(modulesYaml.virtualStoreDir)) {
|
|
return modulesYaml.virtualStoreDir
|
|
}
|
|
return path.join(modules, modulesYaml.virtualStoreDir)
|
|
}
|
|
|
|
// eslint-disable-next-line
|
|
const ok = (value: any) => expect(value).toBeTruthy()
|
|
// eslint-disable-next-line
|
|
const notOk = (value: any) => expect(value).toBeFalsy()
|
|
return {
|
|
dir: () => projectPath,
|
|
requireModule (pkgName: string) {
|
|
return require(path.join(modules, pkgName))
|
|
},
|
|
has (pkgName: string, _modulesDir?: string) {
|
|
const md = _modulesDir ? path.join(projectPath, _modulesDir) : modules
|
|
ok(fs.existsSync(path.join(md, pkgName)))
|
|
},
|
|
hasNot (pkgName: string, _modulesDir?: string) {
|
|
const md = _modulesDir ? path.join(projectPath, _modulesDir) : modules
|
|
notOk(fs.existsSync(path.join(md, pkgName)))
|
|
},
|
|
getStorePath () {
|
|
const store = getStoreInstance()
|
|
return store.storePath
|
|
},
|
|
resolve (pkgName: string, version?: string, relativePath?: string) {
|
|
const store = getStoreInstance()
|
|
return store.resolve(pkgName, version, relativePath)
|
|
},
|
|
getPkgIndexFilePath (pkgName: string, version: string): string {
|
|
const store = getStoreInstance()
|
|
return store.getPkgIndexFilePath(pkgName, version)
|
|
},
|
|
cafsHas (pkgName: string, version: string) {
|
|
const store = getStoreInstance()
|
|
store.cafsHas(pkgName, version)
|
|
},
|
|
cafsHasNot (pkgName: string, version: string) {
|
|
const store = getStoreInstance()
|
|
store.cafsHasNot(pkgName, version)
|
|
},
|
|
storeHas (pkgName: string, version?: string) {
|
|
const store = getStoreInstance()
|
|
return store.resolve(pkgName, version)
|
|
},
|
|
storeHasNot (pkgName: string, version?: string) {
|
|
try {
|
|
const store = getStoreInstance()
|
|
store.storeHasNot(pkgName, version)
|
|
} catch (err: unknown) {
|
|
if (util.types.isNativeError(err) && err.message.startsWith('Cannot find module store')) {
|
|
return
|
|
}
|
|
throw err
|
|
}
|
|
},
|
|
isExecutable (pathToExe: string) {
|
|
isExecutable(ok, path.join(modules, pathToExe))
|
|
},
|
|
readCurrentLockfile () {
|
|
try {
|
|
return readYamlFileSync(path.join(getVirtualStoreDir(), 'lock.yaml'))
|
|
} catch (err: unknown) {
|
|
if (util.types.isNativeError(err) && 'code' in err && err.code === 'ENOENT') return null!
|
|
throw err
|
|
}
|
|
},
|
|
readModulesManifest: () => readModulesManifest(modules),
|
|
readLockfile (lockfileName: string = WANTED_LOCKFILE) {
|
|
try {
|
|
const raw = fs.readFileSync(path.join(projectPath, lockfileName), 'utf8')
|
|
// Skip the env lockfile document if present (first document in combined format).
|
|
// Cannot import from @pnpm/lockfile.fs here due to circular dependency.
|
|
let content = raw
|
|
if (raw.startsWith('---\n')) {
|
|
const sep = raw.indexOf('\n---\n')
|
|
content = sep !== -1 ? raw.slice(sep + '\n---\n'.length) : ''
|
|
}
|
|
if (!content.trim()) return null!
|
|
return yaml.load(content) as Required<LockfileFile>
|
|
} catch (err: unknown) {
|
|
if (util.types.isNativeError(err) && 'code' in err && err.code === 'ENOENT') return null!
|
|
throw err
|
|
}
|
|
},
|
|
writePackageJson (pkgJson: object) {
|
|
writePackageSync(projectPath, pkgJson as any) // eslint-disable-line
|
|
},
|
|
}
|
|
}
|
|
|
|
function readModulesManifest (modulesDir: string): Modules {
|
|
try {
|
|
return readYamlFileSync<Modules>(path.join(modulesDir, '.modules.yaml'))
|
|
} catch (err: unknown) {
|
|
if (util.types.isNativeError(err) && 'code' in err && err.code === 'ENOENT') return null!
|
|
throw err
|
|
}
|
|
}
|