"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 logger_1 = require("@pnpm/logger");
const pkgid_to_filename_1 = require("@pnpm/pkgid-to-filename");
const symlink_dependency_1 = require("@pnpm/symlink-dependency");
const dp = require("dependency-path");
const path = require("path");
const pnpm_shrinkwrap_1 = require("pnpm-shrinkwrap");
const R = require("ramda");
function shamefullyFlattenByShrinkwrap(shr, importerId, opts) {
    return __awaiter(this, void 0, void 0, function* () {
        if (!shr.packages)
            return {};
        const shrImporter = shr.importers[importerId];
        const entryNodes = R.toPairs(Object.assign({}, shrImporter.devDependencies, shrImporter.dependencies, shrImporter.optionalDependencies))
            .map((pair) => dp.refToRelative(pair[1], pair[0]))
            .filter((nodeId) => nodeId !== null);
        const deps = getDependencies(shr.packages, entryNodes, new Set(), 0, {
            prefix: opts.prefix,
            registry: opts.defaultRegistry,
            virtualStoreDir: opts.virtualStoreDir,
        });
        return shamefullyFlattenGraph(deps, shrImporter.specifiers, {
            dryRun: false,
            modulesDir: opts.modulesDir,
        });
    });
}
exports.shamefullyFlattenByShrinkwrap = shamefullyFlattenByShrinkwrap;
function getDependencies(pkgSnapshots, depRelPaths, walked, depth, opts) {
    if (depRelPaths.length === 0)
        return [];
    const deps = [];
    let nextDepRelPaths = [];
    for (const depRelPath of depRelPaths) {
        if (walked.has(depRelPath))
            continue;
        walked.add(depRelPath);
        const pkgSnapshot = pkgSnapshots[depRelPath];
        if (!pkgSnapshot) {
            if (depRelPath.startsWith('link:'))
                continue;
            // It might make sense to fail if the depPath is not in the skipped list from .modules.yaml
            // However, the skipped list currently contains package IDs, not dep paths.
            logger_1.default.debug({ message: `No entry for "${depRelPath}" in shrinkwrap.yaml` });
            continue;
        }
        const absolutePath = dp.resolve(opts.registry, depRelPath);
        const pkgName = pnpm_shrinkwrap_1.nameVerFromPkgSnapshot(depRelPath, pkgSnapshot).name;
        const modules = path.join(opts.virtualStoreDir, `.${pkgid_to_filename_1.default(absolutePath, opts.prefix)}`, 'node_modules');
        const peripheralLocation = path.join(modules, pkgName);
        const allDeps = Object.assign({}, pkgSnapshot.dependencies, pkgSnapshot.optionalDependencies);
        deps.push({
            absolutePath,
            children: R.keys(allDeps).reduce((children, alias) => {
                children[alias] = dp.refToAbsolute(allDeps[alias], alias, opts.registry);
                return children;
            }, {}),
            depth,
            name: pkgName,
            peripheralLocation,
        });
        nextDepRelPaths = [
            ...nextDepRelPaths,
            ...R.toPairs(Object.assign({}, pkgSnapshot.dependencies, pkgSnapshot.optionalDependencies))
                .map((pair) => dp.refToRelative(pair[1], pair[0]))
                .filter((nodeId) => nodeId !== null),
        ];
    }
    return [
        ...deps,
        ...getDependencies(pkgSnapshots, nextDepRelPaths, walked, depth + 1, opts),
    ];
}
function shamefullyFlattenGraph(depNodes, currentSpecifiers, opts) {
    return __awaiter(this, void 0, void 0, function* () {
        const hoistedAliases = new Set(R.keys(currentSpecifiers));
        const aliasesByDependencyPath = {};
        yield Promise.all(depNodes
            // sort by depth and then alphabetically
            .sort((a, b) => {
            const depthDiff = a.depth - b.depth;
            return depthDiff === 0 ? a.name.localeCompare(b.name) : depthDiff;
        })
            // build the alias map and the id map
            .map((depNode) => {
            for (const childAlias of R.keys(depNode.children)) {
                // if this alias has already been taken, skip it
                if (hoistedAliases.has(childAlias)) {
                    continue;
                }
                hoistedAliases.add(childAlias);
                const childPath = depNode.children[childAlias];
                if (!aliasesByDependencyPath[childPath]) {
                    aliasesByDependencyPath[childPath] = [];
                }
                aliasesByDependencyPath[childPath].push(childAlias);
            }
            return depNode;
        })
            .map((depNode) => __awaiter(this, void 0, void 0, function* () {
            const pkgAliases = aliasesByDependencyPath[depNode.absolutePath];
            if (!pkgAliases) {
                return;
            }
            // TODO when putting logs back in for hoisted packages, you've to put back the condition inside the map,
            // TODO look how it is done in linkPackages
            if (!opts.dryRun) {
                yield Promise.all(pkgAliases.map((pkgAlias) => __awaiter(this, void 0, void 0, function* () {
                    yield symlink_dependency_1.default(depNode.peripheralLocation, opts.modulesDir, pkgAlias);
                })));
            }
        })));
        return aliasesByDependencyPath;
    });
}
exports.default = shamefullyFlattenGraph;
//# sourceMappingURL=index.js.map