"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const R = require("ramda");
const semver = require("semver");
const pnpm_logger_1 = require("pnpm-logger");
const path = require("path");
function default_1(tree, rootNodeIds, topPkgIds, 
    // only the top dependencies that were already installed
    // to avoid warnings about unresolved peer dependencies
    topParents) {
    const pkgsByName = R.fromPairs(topParents.map((parent) => [
        parent.name,
        {
            version: parent.version,
            depth: 0
        }
    ]));
    const nodeIdToResolvedId = {};
    const resolvedTree = {};
    resolvePeersOfChildren(rootNodeIds, pkgsByName, tree, nodeIdToResolvedId, resolvedTree);
    R.values(resolvedTree).forEach(node => {
        node.children = node.children.map(child => nodeIdToResolvedId[child]);
    });
    return resolvedTree;
}
exports.default = default_1;
function resolvePeersOfNode(nodeId, parentPkgs, tree, nodeIdToResolvedId, resolvedTree) {
    const node = tree[nodeId];
    const unknownResolvedPeersOfChildren = resolvePeersOfChildren(node.children, parentPkgs, tree, nodeIdToResolvedId, resolvedTree);
    const resolvedPeers = R.isEmpty(node.pkg.peerDependencies)
        ? []
        : resolvePeers(node, Object.assign({}, parentPkgs, toPkgByName(R.props(node.children, tree))), tree);
    const allResolvedPeers = R.uniq(unknownResolvedPeersOfChildren
        .filter(resolvedPeerNodeId => resolvedPeerNodeId !== nodeId).concat(resolvedPeers));
    let modules;
    let resolvedId;
    if (R.isEmpty(allResolvedPeers)) {
        modules = path.join(node.pkg.localLocation, 'node_modules');
        resolvedId = node.pkg.id;
    }
    else {
        const peersFolder = createPeersFolderName(R.props(allResolvedPeers, tree).map(node => node.pkg));
        modules = path.join(node.pkg.localLocation, peersFolder, 'node_modules');
        resolvedId = `${node.pkg.id}/${peersFolder}`;
    }
    nodeIdToResolvedId[nodeId] = resolvedId;
    if (!resolvedTree[resolvedId] || resolvedTree[resolvedId].depth > node.depth) {
        const hardlinkedLocation = path.join(modules, node.pkg.name);
        resolvedTree[resolvedId] = {
            name: node.pkg.name,
            hasBundledDependencies: node.pkg.hasBundledDependencies,
            fetchingFiles: node.pkg.fetchingFiles,
            resolution: node.pkg.resolution,
            path: node.pkg.path,
            modules,
            hardlinkedLocation,
            optionalDependencies: node.pkg.optionalDependencies,
            children: R.union(node.children, resolvedPeers),
            depth: node.depth,
            resolvedId,
            dev: node.pkg.dev,
            optional: node.pkg.optional,
            id: node.pkg.id,
            installable: node.installable,
        };
    }
    return allResolvedPeers;
}
function resolvePeersOfChildren(children, parentParentPkgs, tree, nodeIdToResolvedId, resolvedTree) {
    const trees = [];
    const unknownResolvedPeersOfChildren = [];
    const parentPkgs = Object.assign({}, parentParentPkgs, toPkgByName(R.props(children, tree)));
    for (const child of children) {
        const allResolvedPeers = resolvePeersOfNode(child, parentPkgs, tree, nodeIdToResolvedId, resolvedTree);
        const unknownResolvedPeersOfChild = allResolvedPeers
            .filter((resolvedPeerNodeId) => children.indexOf(resolvedPeerNodeId) === -1);
        unknownResolvedPeersOfChildren.push.apply(unknownResolvedPeersOfChildren, unknownResolvedPeersOfChild);
    }
    return unknownResolvedPeersOfChildren;
}
function resolvePeers(node, parentPkgs, tree) {
    return R.toPairs(node.pkg.peerDependencies)
        .map(R.apply((peerName, peerVersionRange) => {
        const resolved = parentPkgs[peerName];
        if (!resolved || resolved.nodeId && !tree[resolved.nodeId].installable) {
            pnpm_logger_1.default.warn(`${node.pkg.id} requires a peer of ${peerName}@${peerVersionRange} but none was installed.`);
            return null;
        }
        if (!semver.satisfies(resolved.version, peerVersionRange)) {
            pnpm_logger_1.default.warn(`${node.pkg.id} requires a peer of ${peerName}@${peerVersionRange} but version ${resolved.version} was installed.`);
        }
        if (resolved.depth === 0 || resolved.depth === node.depth + 1) {
            // if the resolved package is a top dependency
            // or the peer dependency is resolved from a regular dependency of the package
            // then there is no need to link it in
            return null;
        }
        return resolved && resolved.nodeId;
    }))
        .filter(Boolean);
}
function toPkgByName(pkgs) {
    const toNameAndPkg = R.map((node) => [
        node.pkg.name,
        {
            version: node.pkg.version,
            nodeId: node.nodeId,
            depth: node.depth,
        }
    ]);
    return R.fromPairs(toNameAndPkg(pkgs));
}
function createPeersFolderName(peers) {
    return peers.map(peer => `${peer.name.replace('/', '!')}@${peer.version}`).sort().join('+');
}
//# sourceMappingURL=resolvePeers.js.map