renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
243 lines • 9.65 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractPnpmFilters = extractPnpmFilters;
exports.findPnpmWorkspace = findPnpmWorkspace;
exports.detectPnpmWorkspaces = detectPnpmWorkspaces;
exports.getPnpmLock = getPnpmLock;
exports.tryParsePnpmWorkspaceYaml = tryParsePnpmWorkspaceYaml;
exports.extractPnpmWorkspaceFile = extractPnpmWorkspaceFile;
const tslib_1 = require("tslib");
const is_1 = tslib_1.__importDefault(require("@sindresorhus/is"));
const find_packages_1 = require("find-packages");
const upath_1 = tslib_1.__importDefault(require("upath"));
const global_1 = require("../../../../config/global");
const logger_1 = require("../../../../logger");
const fs_1 = require("../../../../util/fs");
const yaml_1 = require("../../../../util/yaml");
const schema_1 = require("../schema");
const dependency_1 = require("./common/dependency");
function isPnpmLockfile(obj) {
return is_1.default.plainObject(obj) && 'lockfileVersion' in obj;
}
async function extractPnpmFilters(fileName) {
try {
// TODO: use schema (#9610,#22198)
const contents = (0, yaml_1.parseSingleYaml)((await (0, fs_1.readLocalFile)(fileName, 'utf8')));
if (!Array.isArray(contents.packages) ||
!contents.packages.every((item) => is_1.default.string(item))) {
logger_1.logger.trace({ fileName }, 'Failed to find required "packages" array in pnpm-workspace.yaml');
return undefined;
}
return contents.packages;
}
catch (err) {
logger_1.logger.trace({ fileName, err }, 'Failed to parse pnpm-workspace.yaml');
return undefined;
}
}
async function findPnpmWorkspace(packageFile) {
// search for pnpm-workspace.yaml
const workspaceYamlPath = await (0, fs_1.findLocalSiblingOrParent)(packageFile, 'pnpm-workspace.yaml');
if (!workspaceYamlPath) {
logger_1.logger.trace({ packageFile }, 'Failed to locate pnpm-workspace.yaml in a parent directory.');
return null;
}
// search for pnpm-lock.yaml next to pnpm-workspace.yaml
const pnpmLockfilePath = (0, fs_1.getSiblingFileName)(workspaceYamlPath, 'pnpm-lock.yaml');
if (!(await (0, fs_1.localPathExists)(pnpmLockfilePath))) {
logger_1.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_1.logger.debug(`Detecting pnpm Workspaces`);
const packagePathCache = new Map();
for (const p of packageFiles) {
const { packageFile, managerData } = p;
const pnpmShrinkwrap = managerData?.pnpmShrinkwrap;
// check if pnpmShrinkwrap-file has already been provided
if (pnpmShrinkwrap) {
logger_1.logger.trace({ packageFile, pnpmShrinkwrap }, 'Found an existing pnpm shrinkwrap file; skipping pnpm monorepo check.');
continue;
}
// search for corresponding pnpm workspace
// TODO #22198
const pnpmWorkspace = await findPnpmWorkspace(packageFile);
if (pnpmWorkspace === null) {
continue;
}
const { workspaceYamlPath, lockFilePath } = pnpmWorkspace;
// check if package matches workspace filter
if (!packagePathCache.has(workspaceYamlPath)) {
const filters = await extractPnpmFilters(workspaceYamlPath);
const localDir = global_1.GlobalConfig.get('localDir');
const packages = await (0, find_packages_1.findPackages)(upath_1.default.dirname(upath_1.default.join(localDir, workspaceYamlPath)), {
patterns: filters,
// Match the ignores used in @pnpm/find-workspace-packages
ignore: ['**/node_modules/**', '**/bower_components/**'],
});
const packagePaths = packages.map((pkg) => upath_1.default.join(pkg.dir, 'package.json'));
packagePathCache.set(workspaceYamlPath, packagePaths);
}
const packagePaths = packagePathCache.get(workspaceYamlPath);
const isPackageInWorkspace = packagePaths?.some((p) => p.endsWith(packageFile));
if (isPackageInWorkspace) {
p.managerData ??= {};
p.managerData.pnpmShrinkwrap = lockFilePath;
}
else {
logger_1.logger.trace({ packageFile, workspaceYamlPath }, `Didn't find the package in the pnpm workspace`);
}
}
}
async function getPnpmLock(filePath) {
try {
const pnpmLockRaw = await (0, fs_1.readLocalFile)(filePath, 'utf8');
if (!pnpmLockRaw) {
throw new Error('Unable to read pnpm-lock.yaml');
}
const lockParsed = (0, yaml_1.parseSingleYaml)(pnpmLockRaw);
if (!isPnpmLockfile(lockParsed)) {
throw new Error('Invalid or empty lockfile');
}
logger_1.logger.trace({ lockParsed }, 'pnpm lockfile parsed');
// field lockfileVersion is type string in lockfileVersion = 6 and type number in < 6
const lockfileVersion = is_1.default.number(lockParsed.lockfileVersion)
? lockParsed.lockfileVersion
: parseFloat(lockParsed.lockfileVersion);
const lockedVersions = getLockedVersions(lockParsed);
const lockedCatalogVersions = getLockedCatalogVersions(lockParsed);
return {
lockedVersionsWithPath: lockedVersions,
lockedVersionsWithCatalog: lockedCatalogVersions,
lockfileVersion,
};
}
catch (err) {
logger_1.logger.debug({ filePath, err }, 'Warning: Exception parsing pnpm lockfile');
return { lockedVersions: {} };
}
}
function getLockedCatalogVersions(lockParsed) {
const lockedVersions = {};
if (is_1.default.nonEmptyObject(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 = {};
// monorepo
if (is_1.default.nonEmptyObject(lockParsed.importers)) {
for (const [importer, imports] of Object.entries(lockParsed.importers)) {
lockedVersions[importer] = getLockedDependencyVersions(imports);
}
}
// normal repo
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 (is_1.default.object(versionCarrier)) {
version = versionCarrier.version;
}
else {
version = versionCarrier;
}
const pkgVersion = version.split('(')[0].trim();
res[depType][pkgName] = pkgVersion;
}
}
return res;
}
function tryParsePnpmWorkspaceYaml(content) {
try {
const data = (0, yaml_1.parseSingleYaml)(content, {
customSchema: schema_1.PnpmWorkspaceFileSchema,
});
return { success: true, data };
}
catch {
return { success: false };
}
}
async function extractPnpmWorkspaceFile(catalogs, packageFile) {
logger_1.logger.trace(`pnpm.extractPnpmWorkspaceFile(${packageFile})`);
const pnpmCatalogs = pnpmCatalogsToArray(catalogs);
const deps = extractPnpmCatalogDeps(pnpmCatalogs);
let pnpmShrinkwrap;
const filePath = (0, fs_1.getSiblingFileName)(packageFile, 'pnpm-lock.yaml');
if (await (0, fs_1.readLocalFile)(filePath, 'utf8')) {
pnpmShrinkwrap = filePath;
}
return {
deps,
managerData: {
pnpmShrinkwrap,
},
};
}
/**
* In order to facilitate matching on specific catalogs, we structure the
* depType as `pnpm.catalog.default`, `pnpm.catalog.react17`, and so on.
*/
function getCatalogDepType(name) {
const CATALOG_DEPENDENCY = 'pnpm.catalog';
return `${CATALOG_DEPENDENCY}.${name}`;
}
function extractPnpmCatalogDeps(catalogs) {
const deps = [];
for (const catalog of catalogs) {
for (const [key, val] of Object.entries(catalog.dependencies)) {
const depType = getCatalogDepType(catalog.name);
const depName = (0, dependency_1.parseDepName)(depType, key);
const dep = {
depType,
depName,
...(0, dependency_1.extractDependency)(depType, depName, val),
prettyDepType: depType,
};
deps.push(dep);
}
}
return deps;
}
function pnpmCatalogsToArray({ catalog: defaultCatalogDeps, catalogs: namedCatalogs, }) {
const result = [];
if (defaultCatalogDeps !== undefined) {
result.push({ name: 'default', dependencies: defaultCatalogDeps });
}
if (!namedCatalogs) {
return result;
}
for (const [name, dependencies] of Object.entries(namedCatalogs)) {
result.push({
name,
dependencies,
});
}
return result;
}
//# sourceMappingURL=pnpm.js.map