mirror of
https://github.com/pnpm/pnpm.git
synced 2026-03-29 12:31:52 -04:00
171 lines
5.0 KiB
TypeScript
Executable File
171 lines
5.0 KiB
TypeScript
Executable File
import assert from 'assert'
|
|
import util from 'util'
|
|
import {
|
|
type RecursiveSummary,
|
|
throwOnCommandFail,
|
|
} from '@pnpm/cli-utils'
|
|
import {
|
|
type Config,
|
|
createProjectConfigRecord,
|
|
getWorkspaceConcurrency,
|
|
} from '@pnpm/config'
|
|
import { logger } from '@pnpm/logger'
|
|
import { sortPackages } from '@pnpm/sort-packages'
|
|
import { createStoreController, type CreateStoreControllerOptions } from '@pnpm/store-connection-manager'
|
|
import { type Project, type ProjectManifest, type ProjectRootDir } from '@pnpm/types'
|
|
import pLimit from 'p-limit'
|
|
import { rebuildProjects as rebuildAll, type RebuildOptions, rebuildSelectedPkgs } from './implementation/index.js'
|
|
|
|
type RecursiveRebuildOpts = CreateStoreControllerOptions & Pick<Config,
|
|
| 'hoistPattern'
|
|
| 'hooks'
|
|
| 'ignorePnpmfile'
|
|
| 'ignoreScripts'
|
|
| 'lockfileDir'
|
|
| 'lockfileOnly'
|
|
| 'nodeLinker'
|
|
| 'packageConfigs'
|
|
| 'rawLocalConfig'
|
|
| 'registries'
|
|
| 'rootProjectManifest'
|
|
| 'rootProjectManifestDir'
|
|
| 'sharedWorkspaceLockfile'
|
|
> & {
|
|
pending?: boolean
|
|
} & Partial<Pick<Config, 'bail' | 'sort' | 'workspaceConcurrency'>>
|
|
|
|
export async function recursiveRebuild (
|
|
allProjects: Project[],
|
|
params: string[],
|
|
opts: RecursiveRebuildOpts & {
|
|
ignoredPackages?: Set<string>
|
|
} & Required<Pick<Config, 'selectedProjectsGraph' | 'workspaceDir'>>
|
|
): Promise<void> {
|
|
if (allProjects.length === 0) {
|
|
// It might make sense to throw an exception in this case
|
|
return
|
|
}
|
|
|
|
const pkgs = Object.values(opts.selectedProjectsGraph).map((wsPkg) => wsPkg.package)
|
|
|
|
if (pkgs.length === 0) {
|
|
return
|
|
}
|
|
const manifestsByPath: { [dir: string]: Omit<Project, 'rootDir' | 'rootDirRealPath'> } = {}
|
|
for (const { rootDir, manifest, writeProjectManifest } of pkgs) {
|
|
manifestsByPath[rootDir] = { manifest, writeProjectManifest }
|
|
}
|
|
|
|
const throwOnFail = throwOnCommandFail.bind(null, 'pnpm recursive rebuild')
|
|
|
|
const chunks = opts.sort !== false
|
|
? sortPackages(opts.selectedProjectsGraph)
|
|
: [Object.keys(opts.selectedProjectsGraph).sort() as ProjectRootDir[]]
|
|
|
|
const store = await createStoreController(opts)
|
|
|
|
const rebuildOpts = Object.assign(opts, {
|
|
ownLifecycleHooksStdio: 'pipe',
|
|
pruneLockfileImporters: ((opts.ignoredPackages == null) || opts.ignoredPackages.size === 0) &&
|
|
pkgs.length === allProjects.length,
|
|
storeController: store.ctrl,
|
|
storeDir: store.dir,
|
|
}) as RebuildOptions
|
|
|
|
const result: RecursiveSummary = {}
|
|
|
|
const projectConfigRecord = createProjectConfigRecord(opts) ?? {}
|
|
|
|
async function getImporters () {
|
|
const importers = [] as Array<{ buildIndex: number, manifest: ProjectManifest, rootDir: ProjectRootDir }>
|
|
await Promise.all(chunks.map(async (prefixes, buildIndex) => {
|
|
if (opts.ignoredPackages != null) {
|
|
prefixes = prefixes.filter((prefix) => !opts.ignoredPackages!.has(prefix))
|
|
}
|
|
return Promise.all(
|
|
prefixes.map(async (prefix) => {
|
|
importers.push({
|
|
buildIndex,
|
|
manifest: manifestsByPath[prefix].manifest,
|
|
rootDir: prefix,
|
|
})
|
|
})
|
|
)
|
|
}))
|
|
return importers
|
|
}
|
|
|
|
const rebuild = (
|
|
params.length === 0
|
|
? rebuildAll
|
|
: (importers: any, opts: any) => rebuildSelectedPkgs(importers, params, opts) // eslint-disable-line
|
|
)
|
|
if (opts.lockfileDir) {
|
|
const importers = await getImporters()
|
|
await rebuild(
|
|
importers,
|
|
{
|
|
...rebuildOpts,
|
|
pending: opts.pending === true,
|
|
}
|
|
)
|
|
return
|
|
}
|
|
const limitRebuild = pLimit(getWorkspaceConcurrency(opts.workspaceConcurrency))
|
|
for (const chunk of chunks) {
|
|
// eslint-disable-next-line no-await-in-loop
|
|
await Promise.all(chunk.map(async (rootDir) =>
|
|
limitRebuild(async () => {
|
|
try {
|
|
if (opts.ignoredPackages?.has(rootDir)) {
|
|
return
|
|
}
|
|
result[rootDir] = { status: 'running' }
|
|
const { manifest } = opts.selectedProjectsGraph[rootDir].package
|
|
const localConfig = manifest.name ? projectConfigRecord[manifest.name] : undefined
|
|
await rebuild(
|
|
[
|
|
{
|
|
buildIndex: 0,
|
|
manifest: manifestsByPath[rootDir].manifest,
|
|
rootDir,
|
|
},
|
|
],
|
|
{
|
|
...rebuildOpts,
|
|
...localConfig,
|
|
dir: rootDir,
|
|
pending: opts.pending === true,
|
|
rawConfig: {
|
|
...rebuildOpts.rawConfig,
|
|
...localConfig,
|
|
},
|
|
}
|
|
)
|
|
result[rootDir].status = 'passed'
|
|
} catch (err: unknown) {
|
|
assert(util.types.isNativeError(err))
|
|
const errWithPrefix = Object.assign(err, {
|
|
prefix: rootDir,
|
|
})
|
|
logger.info(errWithPrefix)
|
|
|
|
if (!opts.bail) {
|
|
result[rootDir] = {
|
|
status: 'failure',
|
|
error: errWithPrefix,
|
|
message: err.message,
|
|
prefix: rootDir,
|
|
}
|
|
return
|
|
}
|
|
|
|
throw err
|
|
}
|
|
})
|
|
))
|
|
}
|
|
|
|
throwOnFail(result)
|
|
}
|