"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const cli_utils_1 = require("@pnpm/cli-utils");
const find_workspace_packages_1 = require("@pnpm/find-workspace-packages");
const logger_1 = require("@pnpm/logger");
const sort_packages_1 = require("@pnpm/sort-packages");
const store_connection_manager_1 = require("@pnpm/store-connection-manager");
const camelcaseKeys = require("camelcase-keys");
const mem = require("mem");
const pLimit = require("p-limit");
const path = require("path");
const readIniFile = require("read-ini-file");
const implementation_1 = require("./implementation");
async function recursive(allProjects, params, opts) {
    var _a;
    if (allProjects.length === 0) {
        // It might make sense to throw an exception in this case
        return;
    }
    const pkgs = Object.values(opts.selectedProjectsGraph).map((wsPkg) => wsPkg.package);
    if (pkgs.length === 0) {
        return;
    }
    const manifestsByPath = {};
    for (const { dir, manifest, writeProjectManifest } of pkgs) {
        manifestsByPath[dir] = { manifest, writeProjectManifest };
    }
    const throwOnFail = cli_utils_1.throwOnCommandFail.bind(null, `pnpm recursive rebuild`);
    const chunks = opts.sort !== false
        ? sort_packages_1.default(opts.selectedProjectsGraph)
        : [Object.keys(opts.selectedProjectsGraph).sort()];
    const store = await store_connection_manager_1.createOrConnectStoreController(opts);
    const workspacePackages = find_workspace_packages_1.arrayOfWorkspacePackagesToMap(allProjects);
    const rebuildOpts = Object.assign(opts, {
        ownLifecycleHooksStdio: 'pipe',
        pruneLockfileImporters: (!opts.ignoredPackages || opts.ignoredPackages.size === 0)
            && pkgs.length === allProjects.length,
        storeController: store.ctrl,
        storeDir: store.dir,
        workspacePackages,
    });
    const result = {
        fails: [],
        passes: 0,
    };
    const memReadLocalConfig = mem(readLocalConfig);
    async function getImporters() {
        const importers = [];
        await Promise.all(chunks.map((prefixes, buildIndex) => {
            if (opts.ignoredPackages) {
                prefixes = prefixes.filter((prefix) => !opts.ignoredPackages.has(prefix));
            }
            return Promise.all(prefixes.map(async (prefix) => {
                importers.push({
                    buildIndex,
                    manifest: manifestsByPath[prefix].manifest,
                    rootDir: prefix,
                });
            }));
        }));
        return importers;
    }
    const rebuild = (params.length === 0
        ? implementation_1.rebuild
        : (importers, opts) => implementation_1.rebuildPkgs(importers, params, opts) // tslint:disable-line
    );
    if (opts.lockfileDir) {
        const importers = await getImporters();
        await rebuild(importers, {
            ...rebuildOpts,
            pending: opts.pending === true,
        });
        return;
    }
    const limitRebuild = pLimit((_a = opts.workspaceConcurrency) !== null && _a !== void 0 ? _a : 4);
    for (const chunk of chunks) {
        await Promise.all(chunk.map((rootDir) => limitRebuild(async () => {
            try {
                if (opts.ignoredPackages && opts.ignoredPackages.has(rootDir)) {
                    return;
                }
                const localConfig = await memReadLocalConfig(rootDir);
                await rebuild([
                    {
                        buildIndex: 0,
                        manifest: manifestsByPath[rootDir].manifest,
                        rootDir,
                    },
                ], {
                    ...rebuildOpts,
                    ...localConfig,
                    dir: rootDir,
                    pending: opts.pending === true,
                    rawConfig: {
                        ...rebuildOpts.rawConfig,
                        ...localConfig,
                    },
                });
                result.passes++;
            }
            catch (err) {
                logger_1.default.info(err);
                if (!opts.bail) {
                    result.fails.push({
                        error: err,
                        message: err.message,
                        prefix: rootDir,
                    });
                    return;
                }
                err['prefix'] = rootDir; // tslint:disable-line:no-string-literal
                throw err;
            }
        })));
    }
    throwOnFail(result);
}
exports.default = recursive;
async function readLocalConfig(prefix) {
    try {
        const ini = await readIniFile(path.join(prefix, '.npmrc'));
        const config = camelcaseKeys(ini);
        if (config.shamefullyFlatten) {
            config.hoistPattern = '*';
            // TODO: print a warning
        }
        if (config.hoist === false) {
            config.hoistPattern = '';
        }
        return config;
    }
    catch (err) {
        if (err.code !== 'ENOENT')
            throw err;
        return {};
    }
}
//# sourceMappingURL=recursive.js.map