"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@pnpm/utils");
const getPreferredVersions_1 = require("./getPreferredVersions");
const resolveDependencies_1 = require("./resolveDependencies");
async function default_1(opts) {
    const directNonLinkedDepsByImporterId = {};
    const linkedDependenciesByImporterId = {};
    const wantedToBeSkippedPackageIds = new Set();
    const ctx = {
        childrenByParentId: {},
        currentLockfile: opts.currentLockfile,
        defaultTag: opts.tag,
        dependenciesTree: {},
        dryRun: opts.dryRun,
        engineStrict: opts.engineStrict,
        force: opts.force,
        hasManifestInLockfile: opts.hasManifestInLockfile,
        lockfileDirectory: opts.lockfileDirectory,
        nodeVersion: opts.nodeVersion,
        outdatedDependencies: {},
        pendingNodes: [],
        pnpmVersion: opts.pnpmVersion,
        readPackageHook: opts.hooks.readPackage,
        registries: opts.registries,
        resolvedPackagesByPackageId: {},
        sideEffectsCache: opts.sideEffectsCache,
        skipped: wantedToBeSkippedPackageIds,
        storeController: opts.storeController,
        updateDepth: typeof opts.updateDepth === 'number' ? opts.updateDepth : -1,
        virtualStoreDir: opts.virtualStoreDir,
        wantedLockfile: opts.wantedLockfile,
    };
    await Promise.all(opts.importers.map(async (importer) => {
        const lockfileImporter = opts.wantedLockfile.importers[importer.id];
        const linkedDependencies = [];
        const resolveCtx = Object.assign({}, ctx, { linkedDependencies, modulesDir: importer.modulesDir, prefix: importer.prefix, resolutionStrategy: opts.resolutionStrategy || 'fast', updateDepth: importer.shamefullyFlatten ? Infinity : ctx.updateDepth });
        const resolveOpts = {
            currentDepth: 0,
            localPackages: opts.localPackages,
            parentDependsOnPeers: true,
            parentNodeId: utils_1.ROOT_NODE_ID,
            preferredVersions: opts.preferredVersions || importer.pkg && getPreferredVersions_1.default(importer.pkg) || {},
            resolvedDependencies: Object.assign({}, lockfileImporter.dependencies, lockfileImporter.devDependencies, lockfileImporter.optionalDependencies),
        };
        const newDirectDeps = await resolveDependencies_1.default(resolveCtx, importer.nonLinkedPackages, resolveOpts);
        // TODO: in a new major version of pnpm (maybe 3)
        // all dependencies should be resolved for all projects
        // even for those that don't use external lockfiles
        if (!importer.usesExternalLockfile || !importer.pkg) {
            directNonLinkedDepsByImporterId[importer.id] = newDirectDeps;
        }
        else {
            directNonLinkedDepsByImporterId[importer.id] = [
                ...newDirectDeps,
                ...await resolveDependencies_1.default(Object.assign({}, resolveCtx, { updateDepth: -1 }), utils_1.getWantedDependencies(importer.pkg).filter((wantedDep) => newDirectDeps.every((newDep) => newDep.alias !== wantedDep.alias)), Object.assign({}, resolveOpts)),
            ];
        }
        linkedDependenciesByImporterId[importer.id] = linkedDependencies;
    }));
    ctx.pendingNodes.forEach((pendingNode) => {
        ctx.dependenciesTree[pendingNode.nodeId] = {
            children: () => buildTree(ctx, pendingNode.nodeId, pendingNode.resolvedPackage.id, ctx.childrenByParentId[pendingNode.resolvedPackage.id], pendingNode.depth + 1, pendingNode.installable),
            depth: pendingNode.depth,
            installable: pendingNode.installable,
            resolvedPackage: pendingNode.resolvedPackage,
        };
    });
    const resolvedImporters = {};
    for (const importer of opts.importers) {
        const directNonLinkedDeps = directNonLinkedDepsByImporterId[importer.id];
        const linkedDependencies = linkedDependenciesByImporterId[importer.id];
        resolvedImporters[importer.id] = {
            directDependencies: [
                ...directNonLinkedDeps
                    .map((dependency) => (Object.assign({}, ctx.dependenciesTree[dependency.nodeId].resolvedPackage, { alias: dependency.alias, normalizedPref: dependency.normalizedPref }))),
                ...linkedDependencies,
            ],
            directNodeIdsByAlias: directNonLinkedDeps
                .reduce((acc, dependency) => {
                acc[dependency.alias] = dependency.nodeId;
                return acc;
            }, {}),
            linkedDependencies,
        };
    }
    return {
        dependenciesTree: ctx.dependenciesTree,
        outdatedDependencies: ctx.outdatedDependencies,
        resolvedImporters,
        resolvedPackagesByPackageId: ctx.resolvedPackagesByPackageId,
        wantedToBeSkippedPackageIds,
    };
}
exports.default = default_1;
function buildTree(ctx, parentNodeId, parentId, children, depth, installable) {
    const childrenNodeIds = {};
    for (const child of children) {
        if (utils_1.nodeIdContainsSequence(parentNodeId, parentId, child.pkgId)) {
            continue;
        }
        const childNodeId = utils_1.createNodeId(parentNodeId, child.pkgId);
        childrenNodeIds[child.alias] = childNodeId;
        installable = installable && !ctx.skipped.has(child.pkgId);
        ctx.dependenciesTree[childNodeId] = {
            children: () => buildTree(ctx, childNodeId, child.pkgId, ctx.childrenByParentId[child.pkgId], depth + 1, installable),
            depth,
            installable,
            resolvedPackage: ctx.resolvedPackagesByPackageId[child.pkgId],
        };
    }
    return childrenNodeIds;
}
//# sourceMappingURL=index.js.map