"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(pkgsMap, topPkgIds) {
    const tree = createTree(pkgsMap, topPkgIds, []);
    const pkgsByName = toPkgByName(R.props(tree.rootNodeIds, tree.nodes));
    const resolvedTreeMap = R.reduce(R.merge, {}, tree.rootNodeIds.map(rootNodeId => resolvePeersOfNode(rootNodeId, pkgsByName, tree.nodes)));
    return resolvedTreeMap;
}
exports.default = default_1;
function createTree(pkgsMap, pkgIds, keypath) {
    return R.props(pkgIds, pkgsMap)
        .reduce((acc, pkg) => {
        const node = createTreeNode(pkgsMap, pkg, keypath);
        return {
            rootNodeIds: R.append(node.nodeId, acc.rootNodeIds),
            nodes: R.merge(acc.nodes, node.childNodes)
        };
    }, { rootNodeIds: [], nodes: {} });
}
function createTreeNode(pkgsMap, pkg, keypath) {
    const nonCircularDeps = getNonCircularDependencies(pkg.id, pkg.dependencies, keypath);
    const newKeypath = R.append(pkg.id, keypath);
    const nodeId = newKeypath.join('/');
    const tree = createTree(pkgsMap, nonCircularDeps, newKeypath);
    return {
        nodeId,
        childNodes: R.merge(R.objOf(nodeId, {
            pkg,
            nodeId,
            children: tree.rootNodeIds,
            depth: keypath.length,
        }), tree.nodes)
    };
}
function getNonCircularDependencies(parentId, dependencyIds, keypath) {
    const relations = R.aperture(2, keypath);
    const isCircular = R.partialRight(R.contains, [relations]);
    return dependencyIds.filter(depId => !isCircular([parentId, depId]));
}
function resolvePeersOfNode(nodeId, parentPkgs, tree) {
    const node = tree[nodeId];
    const newParentPkgs = Object.assign({}, parentPkgs, { [node.pkg.name]: { version: node.pkg.version, nodeId: node.nodeId } }, toPkgByName(R.props(node.children, tree)));
    const resolvedPeers = resolvePeers(node.pkg.peerDependencies, node.pkg.id, newParentPkgs);
    const modules = path.join(node.pkg.localLocation, 'node_modules');
    const peerModules = !R.isEmpty(node.pkg.peerDependencies)
        ? path.join(node.pkg.localLocation, createPeersFolderName(R.props(resolvedPeers, tree).map(node => node.pkg)), 'node_modules')
        : undefined;
    const hardlinkedLocation = path.join(peerModules || modules, node.pkg.name);
    return R.reduce(R.merge, R.objOf(nodeId, {
        name: node.pkg.name,
        hasBundledDependencies: node.pkg.hasBundledDependencies,
        fetchingFiles: node.pkg.fetchingFiles,
        path: node.pkg.path,
        peerModules,
        modules,
        hardlinkedLocation,
        resolvedPeers,
        children: node.children,
        depth: node.depth,
        id: node.pkg.id,
    }), node.children.map(child => resolvePeersOfNode(child, newParentPkgs, tree)));
}
function resolvePeers(peerDependencies, pkgId, parentPkgs) {
    return R.toPairs(peerDependencies)
        .map(R.apply((peerName, peerVersionRange) => {
        const resolved = parentPkgs[peerName];
        if (!resolved) {
            pnpm_logger_1.default.warn(`${pkgId} requires a peer of ${peerName}@${peerVersionRange} but none was installed.`);
            return null;
        }
        if (!semver.satisfies(resolved.version, peerVersionRange)) {
            pnpm_logger_1.default.warn(`${pkgId} requires a peer of ${peerName}@${peerVersionRange} but version ${resolved.version} was installed.`);
        }
        return resolved && resolved.nodeId;
    }))
        .filter(Boolean);
}
function toPkgByName(pkgs) {
    const toNameAndPkg = R.map((node) => [
        node.pkg.name,
        { version: node.pkg.version, nodeId: node.nodeId }
    ]);
    return R.fromPairs(toNameAndPkg(pkgs));
}
function createPeersFolderName(peers) {
    return peers.map(peer => `${peer.name.replace('/', '!')}@${peer.version}`).sort().join('+');
}
//# sourceMappingURL=resolvePeers.js.map