UNPKG

renovate

Version:

Automated dependency updates. Flexible so you don't need to be.

177 lines (176 loc) • 6.71 kB
import { GlobalConfig } from "../../../../config/global.js"; import { logger } from "../../../../logger/index.js"; import { parseSingleYaml, parseYaml } from "../../../../util/yaml.js"; import { findLocalSiblingOrParent, getSiblingFileName, localPathExists, readLocalFile } from "../../../../util/fs/index.js"; import { extractDependency } from "./common/dependency.js"; import { pnpmWorkspaceOverrides } from "../dep-types.js"; import { extractCatalogDeps } from "./common/catalogs.js"; import { isNonEmptyObject, isNumber, isObject, isPlainObject, isString } from "@sindresorhus/is"; import upath from "upath"; import { parsePkgAndParentSelector } from "@pnpm/parse-overrides"; import { findPackages } from "find-packages"; //#region lib/modules/manager/npm/extract/pnpm.ts function isPnpmLockfile(obj) { return isPlainObject(obj) && "lockfileVersion" in obj; } async function extractPnpmFilters(fileName) { try { const contents = parseSingleYaml(await readLocalFile(fileName, "utf8")); if (!Array.isArray(contents.packages) || !contents.packages.every((item) => isString(item))) { logger.trace({ fileName }, "Failed to find required \"packages\" array in pnpm-workspace.yaml"); return; } return contents.packages; } catch (err) { logger.trace({ fileName, err }, "Failed to parse pnpm-workspace.yaml"); return; } } async function findPnpmWorkspace(packageFile) { const workspaceYamlPath = await findLocalSiblingOrParent(packageFile, "pnpm-workspace.yaml"); if (!workspaceYamlPath) { logger.trace({ packageFile }, "Failed to locate pnpm-workspace.yaml in a parent directory."); return null; } const pnpmLockfilePath = getSiblingFileName(workspaceYamlPath, "pnpm-lock.yaml"); if (!await localPathExists(pnpmLockfilePath)) { logger.trace({ workspaceYamlPath, packageFile }, "Failed to find a pnpm-lock.yaml sibling for the workspace."); return null; } return { lockFilePath: pnpmLockfilePath, workspaceYamlPath }; } async function detectPnpmWorkspaces(packageFiles) { logger.debug(`Detecting pnpm Workspaces`); const packagePathCache = /* @__PURE__ */ new Map(); for (const p of packageFiles) { const { packageFile, managerData } = p; const pnpmShrinkwrap = managerData?.pnpmShrinkwrap; if (pnpmShrinkwrap) { logger.trace({ packageFile, pnpmShrinkwrap }, "Found an existing pnpm shrinkwrap file; skipping pnpm monorepo check."); continue; } const pnpmWorkspace = await findPnpmWorkspace(packageFile); if (pnpmWorkspace === null) continue; const { workspaceYamlPath, lockFilePath } = pnpmWorkspace; if (!packagePathCache.has(workspaceYamlPath)) { const filters = await extractPnpmFilters(workspaceYamlPath); const localDir = GlobalConfig.get("localDir"); const packagePaths = (await findPackages(upath.dirname(upath.join(localDir, workspaceYamlPath)), { patterns: filters, ignore: ["**/node_modules/**", "**/bower_components/**"] })).map((pkg) => upath.join(pkg.dir, "package.json")); packagePathCache.set(workspaceYamlPath, packagePaths); } if (packagePathCache.get(workspaceYamlPath)?.some((p) => p.endsWith(packageFile))) { p.managerData ??= {}; p.managerData.pnpmShrinkwrap = lockFilePath; } else logger.trace({ packageFile, workspaceYamlPath }, `Didn't find the package in the pnpm workspace`); } } async function getPnpmLock(filePath) { try { const pnpmLockRaw = await readLocalFile(filePath, "utf8"); if (!pnpmLockRaw) throw new Error("Unable to read pnpm-lock.yaml"); const lockParsed = parseYaml(pnpmLockRaw).at(-1); if (!isPnpmLockfile(lockParsed)) throw new Error("Invalid or empty lockfile"); logger.trace({ lockParsed }, "pnpm lockfile parsed"); const lockfileVersion = isNumber(lockParsed.lockfileVersion) ? lockParsed.lockfileVersion : parseFloat(lockParsed.lockfileVersion); return { lockedVersionsWithPath: getLockedVersions(lockParsed), lockedVersionsWithCatalog: getLockedCatalogVersions(lockParsed), lockfileVersion }; } catch (err) { logger.debug({ filePath, err }, "Warning: Exception parsing pnpm lockfile"); return { lockedVersions: {} }; } } function getLockedCatalogVersions(lockParsed) { const lockedVersions = {}; if (isNonEmptyObject(lockParsed.catalogs)) for (const [catalog, dependencies] of Object.entries(lockParsed.catalogs)) { const versions = {}; for (const [dep, versionCarrier] of Object.entries(dependencies)) versions[dep] = versionCarrier.version; lockedVersions[catalog] = versions; } return lockedVersions; } function getLockedVersions(lockParsed) { const lockedVersions = {}; if (isNonEmptyObject(lockParsed.importers)) for (const [importer, imports] of Object.entries(lockParsed.importers)) lockedVersions[importer] = getLockedDependencyVersions(imports); else lockedVersions["."] = getLockedDependencyVersions(lockParsed); return lockedVersions; } function getLockedDependencyVersions(obj) { const dependencyTypes = [ "dependencies", "devDependencies", "optionalDependencies" ]; const res = {}; for (const depType of dependencyTypes) { res[depType] = {}; for (const [pkgName, versionCarrier] of Object.entries(obj[depType] ?? {})) { let version; if (isObject(versionCarrier)) version = versionCarrier.version; else version = versionCarrier; const pkgVersion = version.split("(")[0].trim(); res[depType][pkgName] = pkgVersion; } } return res; } async function extractPnpmWorkspaceFile(workspaceFile, packageFile) { logger.trace(`pnpm.extractPnpmWorkspaceFile(${packageFile})`); const deps = extractCatalogDeps(pnpmCatalogsToArray(workspaceFile)); if (workspaceFile.overrides) for (const [overridesKey, overridesVal] of Object.entries(workspaceFile.overrides)) { const packageName = parsePkgAndParentSelector(overridesKey).targetPkg.name; const depType = pnpmWorkspaceOverrides; deps.push({ depName: overridesKey, packageName, depType, ...extractDependency(depType, packageName, overridesVal) }); } let pnpmShrinkwrap; const filePath = getSiblingFileName(packageFile, "pnpm-lock.yaml"); if (await readLocalFile(filePath, "utf8")) pnpmShrinkwrap = filePath; return { deps, managerData: { pnpmShrinkwrap } }; } function pnpmCatalogsToArray({ catalog: defaultCatalogDeps, catalogs: namedCatalogs }) { const result = []; if (defaultCatalogDeps !== void 0) result.push({ name: "default", dependencies: defaultCatalogDeps }); if (!namedCatalogs) return result; for (const [name, dependencies] of Object.entries(namedCatalogs)) result.push({ name, dependencies }); return result; } //#endregion export { detectPnpmWorkspaces, extractPnpmWorkspaceFile, getPnpmLock }; //# sourceMappingURL=pnpm.js.map