UNPKG

isolate-package

Version:

Isolate monorepo packages to form a self-contained deployable unit

130 lines (108 loc) 4.01 kB
import fs from "fs-extra"; import path from "node:path"; import { readWantedLockfile as readWantedLockfile_v8 } from "pnpm_lockfile_file_v8"; import { readWantedLockfile as readWantedLockfile_v9 } from "pnpm_lockfile_file_v9"; import { useLogger } from "~/lib/logger"; import { usePackageManager } from "~/lib/package-manager"; import type { PackageManifest, PatchFile } from "~/lib/types"; import { filterPatchedDependencies, getRootRelativeLogPath, isRushWorkspace, readTypedJson, } from "~/lib/utils"; export async function copyPatches({ workspaceRootDir, targetPackageManifest, isolateDir, includeDevDependencies, }: { workspaceRootDir: string; targetPackageManifest: PackageManifest; isolateDir: string; includeDevDependencies: boolean; }): Promise<Record<string, PatchFile>> { const log = useLogger(); let workspaceRootManifest: PackageManifest; try { workspaceRootManifest = await readTypedJson<PackageManifest>( path.join(workspaceRootDir, "package.json"), ); } catch (error) { log.warn( `Could not read workspace root package.json: ${error instanceof Error ? error.message : String(error)}`, ); return {}; } const patchedDependencies = workspaceRootManifest.pnpm?.patchedDependencies; if (!patchedDependencies || Object.keys(patchedDependencies).length === 0) { log.debug("No patched dependencies found in workspace root package.json"); return {}; } log.debug( `Found ${Object.keys(patchedDependencies).length} patched dependencies in workspace`, ); const filteredPatches = filterPatchedDependencies({ patchedDependencies, targetPackageManifest, includeDevDependencies, }); if (!filteredPatches) { return {}; } /** Read the lockfile to get the hashes for each patch */ const lockfilePatchedDependencies = await readLockfilePatchedDependencies(workspaceRootDir); const copiedPatches: Record<string, PatchFile> = {}; for (const [packageSpec, patchPath] of Object.entries(filteredPatches)) { const sourcePatchPath = path.resolve(workspaceRootDir, patchPath); if (!fs.existsSync(sourcePatchPath)) { log.warn( `Patch file not found: ${getRootRelativeLogPath(sourcePatchPath, workspaceRootDir)}`, ); continue; } /** Preserve original folder structure */ const targetPatchPath = path.join(isolateDir, patchPath); await fs.ensureDir(path.dirname(targetPatchPath)); await fs.copy(sourcePatchPath, targetPatchPath); log.debug(`Copied patch for ${packageSpec}: ${patchPath}`); /** Get the hash from the original lockfile, or use empty string if not found */ const originalPatchFile = lockfilePatchedDependencies?.[packageSpec]; const hash = originalPatchFile?.hash ?? ""; if (!hash) { log.warn(`No hash found for patch ${packageSpec} in lockfile`); } copiedPatches[packageSpec] = { path: patchPath, hash, }; } if (Object.keys(copiedPatches).length > 0) { log.debug(`Copied ${Object.keys(copiedPatches).length} patch files`); } return copiedPatches; } /** * Read the patchedDependencies from the original lockfile to get the hashes. * Since the file content is the same after copying, the hash remains valid. */ async function readLockfilePatchedDependencies( workspaceRootDir: string, ): Promise<Record<string, PatchFile> | undefined> { try { const { majorVersion } = usePackageManager(); const useVersion9 = majorVersion >= 9; const isRush = isRushWorkspace(workspaceRootDir); const lockfileDir = isRush ? path.join(workspaceRootDir, "common/config/rush") : workspaceRootDir; const lockfile = useVersion9 ? await readWantedLockfile_v9(lockfileDir, { ignoreIncompatible: false }) : await readWantedLockfile_v8(lockfileDir, { ignoreIncompatible: false }); return lockfile?.patchedDependencies; } catch { /** Package manager not detected or lockfile not readable */ return undefined; } }