"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPnpmLock = exports.detectPnpmWorkspaces = exports.findPnpmWorkspace = exports.extractPnpmFilters = void 0;
const tslib_1 = require("tslib");
const is_1 = tslib_1.__importDefault(require("@sindresorhus/is"));
const find_packages_1 = require("find-packages");
const js_yaml_1 = require("js-yaml");
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");
function isPnpmLockfile(obj) {
    return is_1.default.plainObject(obj) && 'lockfileVersion' in obj;
}
async function extractPnpmFilters(fileName) {
    try {
        // TODO #7154
        const contents = (0, js_yaml_1.load)((await (0, fs_1.readLocalFile)(fileName, 'utf8')), {
            json: true,
        });
        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;
    }
}
exports.extractPnpmFilters = extractPnpmFilters;
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,
    };
}
exports.findPnpmWorkspace = findPnpmWorkspace;
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;
        // 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 #7154
        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();
            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`);
        }
    }
}
exports.detectPnpmWorkspaces = detectPnpmWorkspaces;
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, js_yaml_1.load)(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);
        return {
            lockedVersionsWithPath: lockedVersions,
            lockfileVersion,
        };
    }
    catch (err) {
        logger_1.logger.debug({ filePath, err }, 'Warning: Exception parsing pnpm lockfile');
        return { lockedVersions: {} };
    }
}
exports.getPnpmLock = getPnpmLock;
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;
}
//# sourceMappingURL=pnpm.js.map