"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const core_loggers_1 = require("@pnpm/core-loggers");
const error_1 = require("@pnpm/error");
const logger_1 = require("@pnpm/logger");
const read_importers_context_1 = require("@pnpm/read-importers-context");
const types_1 = require("@pnpm/types");
const makeDir = require("make-dir");
const removeAllExceptOuterLinks = require("remove-all-except-outer-links");
const checkCompatibility_1 = require("./checkCompatibility");
const readLockfiles_1 = require("./readLockfiles");
async function getContext(importers, opts) {
    const importersContext = await read_importers_context_1.default(importers, opts.lockfileDirectory, {
        shamefullyFlatten: opts.shamefullyFlatten,
    });
    if (importersContext.modules) {
        await validateNodeModules(importersContext.modules, importersContext.importers, {
            force: opts.force,
            include: opts.include,
            independentLeaves: opts.independentLeaves,
            lockfileDirectory: opts.lockfileDirectory,
            store: opts.store,
        });
    }
    await makeDir(opts.store);
    importers.forEach((importer) => {
        core_loggers_1.packageJsonLogger.debug({
            initial: importer.manifest,
            prefix: importer.prefix,
        });
    });
    if (opts.hooks && opts.hooks.readPackage) {
        importers = importers.map((importer) => (Object.assign({}, importer, { manifest: opts.hooks.readPackage(importer.manifest) })));
    }
    const ctx = Object.assign({ importers: importersContext.importers, include: opts.include || importersContext.include, lockfileDirectory: opts.lockfileDirectory, modulesFile: importersContext.modules, pendingBuilds: importersContext.pendingBuilds, registries: Object.assign({}, opts.registries, importersContext.registries), skipped: importersContext.skipped, storePath: opts.store, virtualStoreDir: importersContext.virtualStoreDir }, await readLockfiles_1.default({
        force: opts.force,
        forceSharedLockfile: opts.forceSharedLockfile,
        importers: importersContext.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 error_1.default('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 error_1.default('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 error_1.default('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 error_1.default('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, modulesDir: 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 error_1.default('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(manifest, opts) {
    const { importers, include, modules, pendingBuilds, registries, skipped, virtualStoreDir, } = await read_importers_context_1.default([
        {
            prefix: opts.prefix,
        },
    ], opts.lockfileDirectory, {
        shamefullyFlatten: opts.shamefullyFlatten,
    });
    const storePath = opts.store;
    const importer = importers[0];
    const modulesDir = importer.modulesDir;
    const importerId = importer.id;
    if (modules) {
        await validateNodeModules(modules, importers, {
            force: opts.force,
            include: opts.include,
            independentLeaves: opts.independentLeaves,
            lockfileDirectory: opts.lockfileDirectory,
            store: opts.store,
        });
    }
    await makeDir(storePath);
    const ctx = Object.assign({ hoistedAliases: importer.hoistedAliases, importerId, include: opts.include || include, lockfileDirectory: opts.lockfileDirectory, manifest: opts.hooks && opts.hooks.readPackage ? opts.hooks.readPackage(manifest) : manifest, modulesDir, modulesFile: modules, pendingBuilds, prefix: opts.prefix, registries: Object.assign({}, opts.registries, registries), skipped,
        storePath,
        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: manifest,
        prefix: opts.prefix,
    });
    return ctx;
}
exports.getContextForSingleImporter = getContextForSingleImporter;
