"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 assert = require("assert");
const dependency_path_1 = require("dependency-path");
const pnpm_shrinkwrap_1 = require("pnpm-shrinkwrap");
const semver = require("semver");
function forPackages(packages, projectPath, opts) {
    assert(packages, 'packages should be defined');
    if (!packages.length)
        return [];
    return dependenciesHierarchy(projectPath, packages, opts);
}
exports.forPackages = forPackages;
function default_1(projectPath, opts) {
    return dependenciesHierarchy(projectPath, [], opts);
}
exports.default = default_1;
function dependenciesHierarchy(projectPath, searched, maybeOpts) {
    return __awaiter(this, void 0, void 0, function* () {
        const shrinkwrap = yield pnpm_shrinkwrap_1.readCurrent(projectPath, { ignoreIncompatible: false });
        if (!shrinkwrap)
            return [];
        const opts = Object.assign({ depth: 0, only: undefined }, maybeOpts);
        const topDeps = getTopDependencies(shrinkwrap, opts);
        if (!topDeps)
            return [];
        const getChildrenTree = getTree.bind(null, {
            currentDepth: 1,
            maxDepth: opts.depth,
            prod: opts.only === 'prod',
            registry: shrinkwrap.registry,
            searched,
        }, shrinkwrap.packages);
        const result = [];
        Object.keys(topDeps).forEach((depName) => {
            const pkgPath = dependency_path_1.refToAbsolute(topDeps[depName], depName, shrinkwrap.registry);
            const pkg = {
                name: depName,
                path: pkgPath || topDeps[depName],
                version: topDeps[depName],
            };
            let newEntry = null;
            const matchedSearched = searched.length && matches(searched, pkg);
            if (pkgPath === null) {
                newEntry = { pkg };
            }
            else {
                const relativeId = dependency_path_1.refToRelative(topDeps[depName], depName);
                const dependencies = getChildrenTree([relativeId], relativeId);
                if (dependencies.length) {
                    newEntry = {
                        dependencies,
                        pkg,
                    };
                }
                else if (!searched.length || matches(searched, pkg)) {
                    newEntry = { pkg };
                }
            }
            if (newEntry) {
                if (matchedSearched) {
                    newEntry.searched = true;
                }
                result.push(newEntry);
            }
        });
        return result;
    });
}
function getTopDependencies(shrinkwrap, opts) {
    switch (opts.only) {
        case 'prod':
            return shrinkwrap.importers['.'].dependencies;
        case 'dev':
            return shrinkwrap.importers['.'].devDependencies;
        default:
            return Object.assign({}, shrinkwrap.importers['.'].dependencies, shrinkwrap.importers['.'].devDependencies, shrinkwrap.importers['.'].optionalDependencies);
    }
}
function getTree(opts, packages, keypath, parentId) {
    if (opts.currentDepth > opts.maxDepth || !packages || !packages[parentId])
        return [];
    const deps = opts.prod
        ? packages[parentId].dependencies
        : Object.assign({}, packages[parentId].dependencies, packages[parentId].optionalDependencies);
    if (!deps)
        return [];
    const getChildrenTree = getTree.bind(null, Object.assign({}, opts, {
        currentDepth: opts.currentDepth + 1,
    }), packages);
    const result = [];
    Object.keys(deps).forEach((depName) => {
        const pkgPath = dependency_path_1.refToAbsolute(deps[depName], depName, opts.registry);
        const pkg = {
            name: depName,
            path: pkgPath || deps[depName],
            version: deps[depName],
        };
        let circular;
        const matchedSearched = opts.searched.length && matches(opts.searched, pkg);
        let newEntry = null;
        if (pkgPath === null) {
            circular = false;
            newEntry = { pkg };
        }
        else {
            const relativeId = dependency_path_1.refToRelative(deps[depName], depName); // we know for sure that relative is not null if pkgPath is not null
            circular = keypath.indexOf(relativeId) !== -1;
            const dependencies = circular ? [] : getChildrenTree(keypath.concat([relativeId]), relativeId);
            if (dependencies.length) {
                newEntry = {
                    dependencies,
                    pkg,
                };
            }
            else if (!opts.searched.length || matchedSearched) {
                newEntry = { pkg };
            }
        }
        if (newEntry) {
            if (circular) {
                newEntry.circular = true;
            }
            if (matchedSearched) {
                newEntry.searched = true;
            }
            result.push(newEntry);
        }
    });
    return result;
}
function matches(searched, pkg) {
    return searched.some((searchedPkg) => {
        if (typeof searchedPkg === 'string') {
            return pkg.name === searchedPkg;
        }
        return searchedPkg.name === pkg.name &&
            semver.satisfies(pkg.version, searchedPkg.range);
    });
}
//# sourceMappingURL=index.js.map