"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const npm_resolver_1 = require("@pnpm/npm-resolver");
const read_package_json_1 = require("@pnpm/read-package-json");
const store_path_1 = require("@pnpm/store-path");
const types_1 = require("@pnpm/types");
const utils_1 = require("@pnpm/utils");
const dp = require("dependency-path");
const pnpm_shrinkwrap_1 = require("pnpm-shrinkwrap");
function default_1(pkgPath, opts) {
    return __awaiter(this, void 0, void 0, function* () {
        return _outdated([], pkgPath, opts);
    });
}
exports.default = default_1;
function forPackages(packages, pkgPath, opts) {
    return __awaiter(this, void 0, void 0, function* () {
        return _outdated(packages, pkgPath, opts);
    });
}
exports.forPackages = forPackages;
function _outdated(forPkgs, pkgPath, opts) {
    return __awaiter(this, void 0, void 0, function* () {
        const registries = utils_1.normalizeRegistries(opts.registries);
        const shrinkwrapDirectory = opts.shrinkwrapDirectory || pkgPath;
        const pkg = yield read_package_json_1.fromDir(pkgPath);
        if (packageHasNoDeps(pkg))
            return [];
        const wantedShrinkwrap = (yield pnpm_shrinkwrap_1.readWanted(shrinkwrapDirectory, { ignoreIncompatible: false }))
            || (yield pnpm_shrinkwrap_1.readCurrent(shrinkwrapDirectory, { ignoreIncompatible: false }));
        if (!wantedShrinkwrap) {
            throw new Error('No shrinkwrapfile in this directory. Run `pnpm install` to generate one.');
        }
        const storePath = yield store_path_1.default(pkgPath, opts.store);
        const importerId = pnpm_shrinkwrap_1.getImporterId(shrinkwrapDirectory, pkgPath);
        const currentShrinkwrap = (yield pnpm_shrinkwrap_1.readCurrent(shrinkwrapDirectory, { ignoreIncompatible: false })) || { importers: { [importerId]: {} } };
        const resolve = npm_resolver_1.default({
            fetchRetries: opts.fetchRetries,
            fetchRetryFactor: opts.fetchRetryFactor,
            fetchRetryMaxtimeout: opts.fetchRetryMaxtimeout,
            fetchRetryMintimeout: opts.fetchRetryMintimeout,
            metaCache: new Map(),
            offline: opts.offline,
            rawNpmConfig: opts.rawNpmConfig,
            store: storePath,
        });
        const outdated = [];
        yield Promise.all(types_1.DEPENDENCIES_FIELDS.map((depType) => __awaiter(this, void 0, void 0, function* () {
            if (!wantedShrinkwrap.importers[importerId][depType])
                return;
            let pkgs = Object.keys(wantedShrinkwrap.importers[importerId][depType]);
            if (forPkgs.length) {
                pkgs = pkgs.filter((pkgName) => forPkgs.indexOf(pkgName) !== -1);
            }
            yield Promise.all(pkgs.map((packageName) => __awaiter(this, void 0, void 0, function* () {
                const ref = wantedShrinkwrap.importers[importerId][depType][packageName];
                // ignoring linked packages. (For backward compatibility)
                if (ref.startsWith('file:')) {
                    return;
                }
                const relativeDepPath = dp.refToRelative(ref, packageName);
                // ignoring linked packages
                if (relativeDepPath === null)
                    return;
                const pkgSnapshot = wantedShrinkwrap.packages && wantedShrinkwrap.packages[relativeDepPath];
                if (!pkgSnapshot) {
                    throw new Error(`Invalid shrinkwrap.yaml file. ${relativeDepPath} not found in packages field`);
                }
                const currentRef = currentShrinkwrap.importers[importerId][depType][packageName];
                const currentRelative = currentRef && dp.refToRelative(currentRef, packageName);
                const current = currentRelative && dp.parse(currentRelative).version || currentRef;
                const wanted = dp.parse(relativeDepPath).version || ref;
                // It might be not the best solution to check for pkgSnapshot.name
                // TODO: add some other field to distinct packages not from the registry
                if (pkgSnapshot.resolution && (pkgSnapshot.resolution['type'] || pkgSnapshot.name)) { // tslint:disable-line:no-string-literal
                    if (current !== wanted) {
                        outdated.push({
                            current,
                            latest: undefined,
                            packageName,
                            wanted,
                        });
                    }
                    return;
                }
                // TODO: what about aliased dependencies?
                // TODO: what about scoped dependencies?
                const resolution = yield resolve({ alias: packageName, pref: 'latest' }, {
                    registry: registries.default,
                });
                if (!resolution || !resolution.latest)
                    return;
                const latest = resolution.latest;
                if (!current) {
                    outdated.push({
                        latest,
                        packageName,
                        wanted,
                    });
                    return;
                }
                if (current !== wanted || latest !== current) {
                    outdated.push({
                        current,
                        latest,
                        packageName,
                        wanted,
                    });
                }
            })));
        })));
        return outdated.sort((pkg1, pkg2) => pkg1.packageName.localeCompare(pkg2.packageName));
    });
}
// tslint:disable-next-line:no-any
function packageHasNoDeps(pkg) {
    return (!pkg.dependencies || isEmpty(pkg.dependencies))
        && (!pkg.devDependencies || isEmpty(pkg.devDependencies))
        && (!pkg.optionalDependencies || isEmpty(pkg.optionalDependencies));
}
function isEmpty(obj) {
    return Object.keys(obj).length === 0;
}
//# sourceMappingURL=index.js.map