"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const core_loggers_1 = require("@pnpm/core-loggers");
const logger_1 = require("@pnpm/logger");
const read_manifests_1 = require("@pnpm/read-manifests");
const types_1 = require("@pnpm/types");
const utils_1 = require("@pnpm/utils");
const mkdirp = require("mkdirp-promise");
const removeAllExceptOuterLinks = require("remove-all-except-outer-links");
const errorTypes_1 = require("../errorTypes");
const checkCompatibility_1 = require("./checkCompatibility");
const readLockfiles_1 = require("./readLockfiles");
async function getContext(importers, opts) {
    const manifests = await read_manifests_1.default(importers, opts.lockfileDirectory, {
        shamefullyFlatten: opts.shamefullyFlatten,
    });
    if (manifests.modules) {
        await validateNodeModules(manifests.modules, manifests.importers, {
            force: opts.force,
            include: opts.include,
            independentLeaves: opts.independentLeaves,
            lockfileDirectory: opts.lockfileDirectory,
            store: opts.store,
        });
    }
    await mkdirp(opts.store);
    manifests.importers.forEach((importer) => {
        core_loggers_1.packageJsonLogger.debug({
            initial: importer.pkg,
            prefix: importer.prefix,
        });
    });
    if (opts.hooks && opts.hooks.readPackage) {
        manifests.importers = manifests.importers.map((importer) => (Object.assign({}, importer, { pkg: opts.hooks.readPackage(importer.pkg) })));
    }
    const importerOptionsByPrefix = importers.reduce((prev, curr) => {
        prev[curr.prefix] = curr;
        return prev;
    }, {});
    const ctx = Object.assign({ importers: manifests.importers.map((importer) => (Object.assign({}, importerOptionsByPrefix[importer.prefix], importer))), include: opts.include || manifests.include, lockfileDirectory: opts.lockfileDirectory, modulesFile: manifests.modules, pendingBuilds: manifests.pendingBuilds, registries: Object.assign({}, opts.registries, manifests.registries), skipped: manifests.skipped, storePath: opts.store, virtualStoreDir: manifests.virtualStoreDir }, await readLockfiles_1.default({
        force: opts.force,
        forceSharedLockfile: opts.forceSharedLockfile,
        importers: manifests.importers,
        lockfileDirectory: opts.lockfileDirectory,
        registry: opts.registries.default,
        useLockfile: opts.useLockfile,
    }));
    return ctx;
}
exports.default = getContext;
async function validateNodeModules(modules, importers, opts) {
    if (Boolean(modules.independentLeaves) !== opts.independentLeaves) {
        if (opts.force) {
            await Promise.all(importers.map(async (importer) => {
                logger_1.default.info({
                    message: `Recreating ${importer.modulesDir}`,
                    prefix: importer.prefix,
                });
                try {
                    await removeAllExceptOuterLinks(importer.modulesDir);
                }
                catch (err) {
                    if (err.code !== 'ENOENT')
                        throw err;
                }
            }));
            // TODO: remove the node_modules in the lockfile directory
            return;
        }
        if (modules.independentLeaves) {
            throw new errorTypes_1.PnpmError('ERR_PNPM_INDEPENDENT_LEAVES_WANTED', 'This "node_modules" folder was created using the --independent-leaves option.'
                + ' You must add that option, or else run "pnpm install --force" to recreate the "node_modules" folder.');
        }
        throw new errorTypes_1.PnpmError('ERR_PNPM_INDEPENDENT_LEAVES_NOT_WANTED', 'This "node_modules" folder was created without the --independent-leaves option.'
            + ' You must remove that option, or else "pnpm install --force" to recreate the "node_modules" folder.');
    }
    await Promise.all(importers.map(async (importer) => {
        try {
            if (typeof importer.currentShamefullyFlatten === 'boolean' && importer.currentShamefullyFlatten !== importer.shamefullyFlatten) {
                if (importer.currentShamefullyFlatten) {
                    throw new errorTypes_1.PnpmError('ERR_PNPM_SHAMEFULLY_FLATTEN_WANTED', 'This "node_modules" folder was created using the --shamefully-flatten option.'
                        + ' You must add this option, or else add the --force option to recreate the "node_modules" folder.');
                }
                throw new errorTypes_1.PnpmError('ERR_PNPM_SHAMEFULLY_FLATTEN_NOT_WANTED', 'This "node_modules" folder was created without the --shamefully-flatten option.'
                    + ' You must remove that option, or else add the --force option to recreate the "node_modules" folder.');
            }
            checkCompatibility_1.default(modules, { storePath: opts.store, modulesPath: importer.modulesDir });
            if (opts.lockfileDirectory !== importer.prefix && opts.include && modules.included) {
                for (const depsField of types_1.DEPENDENCIES_FIELDS) {
                    if (opts.include[depsField] !== modules.included[depsField]) {
                        throw new errorTypes_1.PnpmError('ERR_PNPM_INCLUDED_DEPS_CONFLICT', `node_modules (at "${opts.lockfileDirectory}") was installed with ${stringifyIncludedDeps(modules.included)}. ` +
                            `Current install wants ${stringifyIncludedDeps(opts.include)}.`);
                    }
                }
            }
        }
        catch (err) {
            if (!opts.force)
                throw err;
            logger_1.default.info({
                message: `Recreating ${importer.modulesDir}`,
                prefix: importer.prefix,
            });
            try {
                await removeAllExceptOuterLinks(importer.modulesDir);
            }
            catch (err) {
                if (err.code !== 'ENOENT')
                    throw err;
            }
        }
    }));
}
function stringifyIncludedDeps(included) {
    return types_1.DEPENDENCIES_FIELDS.filter((depsField) => included[depsField]).join(', ');
}
async function getContextForSingleImporter(opts) {
    const manifests = await read_manifests_1.default([
        {
            prefix: opts.prefix,
        },
    ], opts.lockfileDirectory, {
        shamefullyFlatten: opts.shamefullyFlatten,
    });
    const storePath = opts.store;
    const importer = manifests.importers[0];
    const modulesDir = importer.modulesDir;
    const importerId = importer.id;
    if (manifests.modules) {
        await validateNodeModules(manifests.modules, manifests.importers, {
            force: opts.force,
            include: opts.include,
            independentLeaves: opts.independentLeaves,
            lockfileDirectory: opts.lockfileDirectory,
            store: opts.store,
        });
    }
    const files = await Promise.all([
        utils_1.safeReadPackageFromDir(opts.prefix),
        mkdirp(storePath),
    ]);
    const pkg = files[0] || {};
    const ctx = Object.assign({ hoistedAliases: importer.hoistedAliases, importerId, include: opts.include || manifests.include, lockfileDirectory: opts.lockfileDirectory, modulesDir, modulesFile: manifests.modules, pendingBuilds: manifests.pendingBuilds, pkg: opts.hooks && opts.hooks.readPackage ? opts.hooks.readPackage(pkg) : pkg, prefix: opts.prefix, registries: Object.assign({}, opts.registries, manifests.registries), skipped: manifests.skipped, storePath, virtualStoreDir: manifests.virtualStoreDir }, await readLockfiles_1.default({
        force: opts.force,
        forceSharedLockfile: opts.forceSharedLockfile,
        importers: [{ id: importerId, prefix: opts.prefix }],
        lockfileDirectory: opts.lockfileDirectory,
        registry: opts.registries.default,
        useLockfile: opts.useLockfile,
    }));
    core_loggers_1.packageJsonLogger.debug({
        initial: pkg,
        prefix: opts.prefix,
    });
    return ctx;
}
exports.getContextForSingleImporter = getContextForSingleImporter;
//# sourceMappingURL=index.js.map