"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const lockfile_utils_1 = require("@pnpm/lockfile-utils");
const read_package_json_1 = require("@pnpm/read-package-json");
const types_1 = require("@pnpm/types");
const p_every_1 = __importDefault(require("p-every"));
const path = require("path");
const R = require("ramda");
const semver = require("semver");
function allProjectsAreUpToDate(projects, opts) {
    var _a, _b;
    const manifestsByDir = opts.workspacePackages ? getWorkspacePackagesByDirectory(opts.workspacePackages) : {};
    const _satisfiesPackageManifest = lockfile_utils_1.satisfiesPackageManifest.bind(null, opts.wantedLockfile);
    const _linkedPackagesAreUpToDate = linkedPackagesAreUpToDate.bind(null, {
        linkWorkspacePackages: opts.linkWorkspacePackages,
        manifestsByDir,
        workspacePackages: opts.workspacePackages,
    });
    return R.equals((_a = opts.wantedLockfile.overrides) !== null && _a !== void 0 ? _a : {}, (_b = opts.overrides) !== null && _b !== void 0 ? _b : {}) && p_every_1.default(projects, (project) => {
        const importer = opts.wantedLockfile.importers[project.id];
        return !hasLocalTarballDepsInRoot(importer) &&
            _satisfiesPackageManifest(project.manifest, project.id) &&
            _linkedPackagesAreUpToDate({
                dir: project.rootDir,
                manifest: project.manifest,
                snapshot: importer,
            });
    });
}
exports.default = allProjectsAreUpToDate;
function getWorkspacePackagesByDirectory(workspacePackages) {
    const workspacePackagesByDirectory = {};
    Object.keys(workspacePackages || {}).forEach((pkgName) => {
        Object.keys(workspacePackages[pkgName] || {}).forEach((pkgVersion) => {
            workspacePackagesByDirectory[workspacePackages[pkgName][pkgVersion].dir] = workspacePackages[pkgName][pkgVersion].manifest;
        });
    });
    return workspacePackagesByDirectory;
}
async function linkedPackagesAreUpToDate({ linkWorkspacePackages, manifestsByDir, workspacePackages, }, project) {
    var _a, _b, _c;
    for (const depField of types_1.DEPENDENCIES_FIELDS) {
        const lockfileDeps = project.snapshot[depField];
        const manifestDeps = project.manifest[depField];
        if (!lockfileDeps || !manifestDeps)
            continue;
        const depNames = Object.keys(lockfileDeps);
        for (const depName of depNames) {
            const currentSpec = manifestDeps[depName];
            if (!currentSpec)
                continue;
            const lockfileRef = lockfileDeps[depName];
            const isLinked = lockfileRef.startsWith('link:');
            if (isLinked &&
                (currentSpec.startsWith('link:') ||
                    currentSpec.startsWith('file:'))) {
                continue;
            }
            const linkedDir = isLinked
                ? path.join(project.dir, lockfileRef.substr(5))
                : (_b = (_a = workspacePackages === null || workspacePackages === void 0 ? void 0 : workspacePackages[depName]) === null || _a === void 0 ? void 0 : _a[lockfileRef]) === null || _b === void 0 ? void 0 : _b.dir;
            if (!linkedDir)
                continue;
            if (!linkWorkspacePackages && !currentSpec.startsWith('workspace:')) {
                // we found a linked dir, but we don't want to use it, because it's not specified as a
                // workspace:x.x.x dependency
                continue;
            }
            const linkedPkg = (_c = manifestsByDir[linkedDir]) !== null && _c !== void 0 ? _c : await read_package_json_1.safeReadPackageFromDir(linkedDir);
            const availableRange = getVersionRange(currentSpec);
            // This should pass the same options to semver as @pnpm/npm-resolver
            const localPackageSatisfiesRange = availableRange === '*' ||
                linkedPkg && semver.satisfies(linkedPkg.version, availableRange, { loose: true });
            if (isLinked !== localPackageSatisfiesRange)
                return false;
        }
    }
    return true;
}
function getVersionRange(spec) {
    if (spec.startsWith('workspace:'))
        return spec.substr(10);
    if (spec.startsWith('npm:')) {
        spec = spec.substr(4);
        const index = spec.indexOf('@', 1);
        if (index === -1)
            return '*';
        return spec.substr(index + 1) || '*';
    }
    return spec;
}
function hasLocalTarballDepsInRoot(importer) {
    var _a, _b, _c;
    return R.any(refIsLocalTarball, Object.values((_a = importer.dependencies) !== null && _a !== void 0 ? _a : {})) ||
        R.any(refIsLocalTarball, Object.values((_b = importer.devDependencies) !== null && _b !== void 0 ? _b : {})) ||
        R.any(refIsLocalTarball, Object.values((_c = importer.optionalDependencies) !== null && _c !== void 0 ? _c : {}));
}
function refIsLocalTarball(ref) {
    return ref.startsWith('file:') && (ref.endsWith('.tgz') || ref.endsWith('.tar.gz') || ref.endsWith('.tar'));
}
//# sourceMappingURL=allProjectsAreUpToDate.js.map