1
// cspell:ignore checkin
4
import { WorkerPool } from '@rushstack/worker-pool/lib/WorkerPool'
5
import { PnpmError } from '@pnpm/error'
6
import { type PackageFilesIndex } from '@pnpm/store.cafs'
7
import { type DependencyManifest } from '@pnpm/types'
9
type TarballExtractMessage,
10
type AddDirToStoreMessage,
12
type SymlinkAllModulesMessage,
13
type HardLinkDirMessage,
16
let workerPool: WorkerPool | undefined
18
export async function restartWorkerPool (): Promise<void> {
20
workerPool = createTarballWorkerPool()
23
export async function finishWorkers (): Promise<void> {
25
await global.finishWorkers?.()
28
function createTarballWorkerPool (): WorkerPool {
29
const maxWorkers = Math.max(2, (os.availableParallelism?.() ?? os.cpus().length) - Math.abs(process.env.PNPM_WORKERS ? parseInt(process.env.PNPM_WORKERS) : 0)) - 1
30
const workerPool = new WorkerPool({
33
workerScriptPath: path.join(__dirname, 'worker.js'),
36
if (global.finishWorkers) {
38
const previous = global.finishWorkers
40
global.finishWorkers = async () => {
42
await workerPool.finishAsync()
46
global.finishWorkers = () => workerPool.finishAsync()
51
interface AddFilesResult {
52
filesIndex: Record<string, string>
53
manifest: DependencyManifest
54
requiresBuild: boolean
57
type AddFilesFromDirOptions = Pick<AddDirToStoreMessage, 'cafsDir' | 'dir' | 'filesIndexFile' | 'sideEffectsCacheKey' | 'readManifest' | 'pkg' | 'files'>
59
export async function addFilesFromDir (opts: AddFilesFromDirOptions): Promise<AddFilesResult> {
61
workerPool = createTarballWorkerPool()
63
const localWorker = await workerPool.checkoutWorkerAsync(true)
64
return new Promise<{ filesIndex: Record<string, string>, manifest: DependencyManifest, requiresBuild: boolean }>((resolve, reject) => {
65
localWorker.once('message', ({ status, error, value }) => {
66
workerPool!.checkinWorker(localWorker)
67
if (status === 'error') {
68
reject(new PnpmError(error.code ?? 'GIT_FETCH_FAILED', error.message as string))
73
localWorker.postMessage({
75
cafsDir: opts.cafsDir,
77
filesIndexFile: opts.filesIndexFile,
78
sideEffectsCacheKey: opts.sideEffectsCacheKey,
79
readManifest: opts.readManifest,
86
export class TarballIntegrityError extends PnpmError {
87
public readonly found: string
88
public readonly expected: string
89
public readonly algorithm: string
90
public readonly sri: string
91
public readonly url: string
101
super('TARBALL_INTEGRITY',
102
`Got unexpected checksum for "${opts.url}". Wanted "${opts.expected}". Got "${opts.found}".`,
104
attempts: opts.attempts,
105
hint: `This error may happen when a package is republished to the registry with the same version.
106
In this case, the metadata in the local pnpm cache will contain the old integrity checksum.
108
If you think that this is the case, then run "pnpm store prune" and rerun the command that failed.
109
"pnpm store prune" will remove your local metadata cache.`,
112
this.found = opts.found
113
this.expected = opts.expected
114
this.algorithm = opts.algorithm
120
type AddFilesFromTarballOptions = Pick<TarballExtractMessage, 'buffer' | 'cafsDir' | 'filesIndexFile' | 'integrity' | 'readManifest' | 'pkg'> & {
124
export async function addFilesFromTarball (opts: AddFilesFromTarballOptions): Promise<AddFilesResult> {
126
workerPool = createTarballWorkerPool()
128
const localWorker = await workerPool.checkoutWorkerAsync(true)
129
return new Promise<{ filesIndex: Record<string, string>, manifest: DependencyManifest, requiresBuild: boolean }>((resolve, reject) => {
130
localWorker.once('message', ({ status, error, value }) => {
131
workerPool!.checkinWorker(localWorker)
132
if (status === 'error') {
133
if (error.type === 'integrity_validation_failed') {
134
reject(new TarballIntegrityError({
140
reject(new PnpmError(error.code ?? 'TARBALL_EXTRACT', `Failed to add tarball from "${opts.url}" to store: ${error.message as string}`))
145
localWorker.postMessage({
148
cafsDir: opts.cafsDir,
149
integrity: opts.integrity,
150
filesIndexFile: opts.filesIndexFile,
151
readManifest: opts.readManifest,
157
export async function readPkgFromCafs (
159
verifyStoreIntegrity: boolean,
160
filesIndexFile: string,
161
readManifest?: boolean
162
): Promise<{ verified: boolean, pkgFilesIndex: PackageFilesIndex, manifest?: DependencyManifest, requiresBuild: boolean }> {
164
workerPool = createTarballWorkerPool()
166
const localWorker = await workerPool.checkoutWorkerAsync(true)
167
return new Promise<{ verified: boolean, pkgFilesIndex: PackageFilesIndex, requiresBuild: boolean }>((resolve, reject) => {
168
localWorker.once('message', ({ status, error, value }) => {
169
workerPool!.checkinWorker(localWorker)
170
if (status === 'error') {
171
reject(new PnpmError(error.code ?? 'READ_FROM_STORE', error.message as string))
176
localWorker.postMessage({
177
type: 'readPkgFromCafs',
181
verifyStoreIntegrity,
186
export async function importPackage (
187
opts: Omit<LinkPkgMessage, 'type'>
188
): Promise<{ isBuilt: boolean, importMethod: string | undefined }> {
190
workerPool = createTarballWorkerPool()
192
const localWorker = await workerPool.checkoutWorkerAsync(true)
193
return new Promise<{ isBuilt: boolean, importMethod: string | undefined }>((resolve, reject) => {
194
localWorker.once('message', ({ status, error, value }: any) => { // eslint-disable-line @typescript-eslint/no-explicit-any
195
workerPool!.checkinWorker(localWorker)
196
if (status === 'error') {
197
reject(new PnpmError(error.code ?? 'LINKING_FAILED', error.message as string))
202
localWorker.postMessage({
209
export async function symlinkAllModules (
210
opts: Omit<SymlinkAllModulesMessage, 'type'>
211
): Promise<{ isBuilt: boolean, importMethod: string | undefined }> {
213
workerPool = createTarballWorkerPool()
215
const localWorker = await workerPool.checkoutWorkerAsync(true)
216
return new Promise<{ isBuilt: boolean, importMethod: string | undefined }>((resolve, reject) => {
217
localWorker.once('message', ({ status, error, value }: any) => { // eslint-disable-line @typescript-eslint/no-explicit-any
218
workerPool!.checkinWorker(localWorker)
219
if (status === 'error') {
220
reject(new PnpmError(error.code ?? 'SYMLINK_FAILED', error.message as string))
225
localWorker.postMessage({
226
type: 'symlinkAllModules',
228
} as SymlinkAllModulesMessage)
232
export async function hardLinkDir (src: string, destDirs: string[]): Promise<void> {
234
workerPool = createTarballWorkerPool()
236
const localWorker = await workerPool.checkoutWorkerAsync(true)
237
await new Promise<void>((resolve, reject) => {
238
localWorker.once('message', ({ status, error }: any) => { // eslint-disable-line @typescript-eslint/no-explicit-any
239
workerPool!.checkinWorker(localWorker)
240
if (status === 'error') {
241
reject(new PnpmError(error.code ?? 'HARDLINK_FAILED', error.message as string))
246
localWorker.postMessage({
250
} as HardLinkDirMessage)