"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_projects_context_1 = require("@pnpm/read-projects-context");
const types_1 = require("@pnpm/types");
const rimraf = require("@zkochan/rimraf");
const makeDir = require("make-dir");
const path = require("path");
const pathAbsolute = require("path-absolute");
const R = require("ramda");
const checkCompatibility_1 = require("./checkCompatibility");
const readLockfiles_1 = require("./readLockfiles");
async function getContext(projects, opts) {
    var _a, _b, _c;
    const modulesDir = (_a = opts.modulesDir) !== null && _a !== void 0 ? _a : 'node_modules';
    let importersContext = await read_projects_context_1.default(projects, { lockfileDir: opts.lockfileDir, modulesDir });
    const virtualStoreDir = pathAbsolute((_b = opts.virtualStoreDir) !== null && _b !== void 0 ? _b : path.join(modulesDir, '.pnpm'), opts.lockfileDir);
    if (importersContext.modules) {
        const { purged } = await validateNodeModules(importersContext.modules, importersContext.projects, {
            currentHoistPattern: importersContext.currentHoistPattern,
            forceNewNodeModules: opts.forceNewNodeModules === true,
            include: opts.include,
            lockfileDir: opts.lockfileDir,
            modulesDir,
            registries: opts.registries,
            storeDir: opts.storeDir,
            virtualStoreDir,
            forceIndependentLeaves: opts.forceIndependentLeaves,
            independentLeaves: opts.independentLeaves,
            forceHoistPattern: opts.forceHoistPattern,
            hoistPattern: opts.hoistPattern,
            forceShamefullyHoist: opts.forceShamefullyHoist,
            shamefullyHoist: opts.shamefullyHoist,
        });
        if (purged) {
            importersContext = await read_projects_context_1.default(projects, {
                lockfileDir: opts.lockfileDir,
                modulesDir,
            });
        }
    }
    await makeDir(opts.storeDir);
    projects.forEach((project) => {
        core_loggers_1.packageManifestLogger.debug({
            initial: project.manifest,
            prefix: project.rootDir,
        });
    });
    if ((_c = opts.hooks) === null || _c === void 0 ? void 0 : _c.readPackage) {
        projects = projects.map((project) => ({
            ...project,
            manifest: opts.hooks.readPackage(project.manifest),
        }));
    }
    const extraBinPaths = [
        ...opts.extraBinPaths || [],
    ];
    const shamefullyHoist = Boolean(typeof importersContext.shamefullyHoist === 'undefined' ? opts.shamefullyHoist : importersContext.shamefullyHoist);
    if (opts.hoistPattern && !shamefullyHoist) {
        extraBinPaths.unshift(path.join(virtualStoreDir, 'node_modules/.bin'));
    }
    const hoistedModulesDir = shamefullyHoist
        ? importersContext.rootModulesDir : path.join(virtualStoreDir, 'node_modules');
    const ctx = {
        extraBinPaths,
        hoistedAliases: importersContext.hoistedAliases,
        hoistedModulesDir,
        hoistPattern: opts.hoistPattern,
        include: opts.include || importersContext.include,
        independentLeaves: Boolean(typeof importersContext.independentLeaves === 'undefined' ? opts.independentLeaves : importersContext.independentLeaves),
        lockfileDir: opts.lockfileDir,
        modulesFile: importersContext.modules,
        pendingBuilds: importersContext.pendingBuilds,
        projects: importersContext.projects,
        registries: {
            ...opts.registries,
            ...importersContext.registries,
        },
        rootModulesDir: importersContext.rootModulesDir,
        shamefullyHoist,
        skipped: importersContext.skipped,
        storeDir: opts.storeDir,
        virtualStoreDir,
        ...await readLockfiles_1.default({
            force: opts.force,
            forceSharedLockfile: opts.forceSharedLockfile,
            lockfileDir: opts.lockfileDir,
            projects: importersContext.projects,
            registry: opts.registries.default,
            useLockfile: opts.useLockfile,
            virtualStoreDir,
        }),
    };
    return ctx;
}
exports.default = getContext;
async function validateNodeModules(modules, projects, opts) {
    const rootProject = projects.find(({ id }) => id === '.');
    if (opts.forceShamefullyHoist && modules.shamefullyHoist !== opts.shamefullyHoist) {
        if (opts.forceNewNodeModules && rootProject) {
            await purgeModulesDirsOfImporter(rootProject);
            return { purged: true };
        }
        if (modules.shamefullyHoist) {
            throw new error_1.default('SHAMEFULLY_HOIST_WANTED', 'This modules directory was created using the --shamefully-hoist option.'
                + ' You must add that option, or else run "pnpm install" to recreate the modules directory.');
        }
        throw new error_1.default('SHAMEFULLY_HOIST_NOT_WANTED', 'This modules directory was created without the --shamefully-hoist option.'
            + ' You must remove that option, or else "pnpm install" to recreate the modules directory.');
    }
    if (opts.forceIndependentLeaves && Boolean(modules.independentLeaves) !== opts.independentLeaves) {
        if (opts.forceNewNodeModules) {
            await Promise.all(projects.map(purgeModulesDirsOfImporter));
            if (!rootProject) {
                await purgeModulesDirsOfImporter({
                    modulesDir: path.join(opts.lockfileDir, opts.modulesDir),
                    rootDir: opts.lockfileDir,
                });
            }
            return { purged: true };
        }
        if (modules.independentLeaves) {
            throw new error_1.default('INDEPENDENT_LEAVES_WANTED', 'This modules directory was created using the --independent-leaves option.'
                + ' You must add that option, or else run "pnpm install" to recreate the modules directory.');
        }
        throw new error_1.default('INDEPENDENT_LEAVES_NOT_WANTED', 'This modules directory was created without the --independent-leaves option.'
            + ' You must remove that option, or else "pnpm install" to recreate the modules directory.');
    }
    let purged = false;
    if (opts.forceHoistPattern && rootProject) {
        try {
            if (!R.equals(opts.currentHoistPattern, (opts.hoistPattern || undefined))) {
                if (opts.currentHoistPattern) {
                    throw new error_1.default('HOISTING_WANTED', 'This modules directory was created using the --hoist-pattern option.'
                        + ' You must add this option, or else add the --force option to recreate the modules directory.');
                }
                throw new error_1.default('HOISTING_NOT_WANTED', 'This modules directory was created without the --hoist-pattern option.'
                    + ' You must remove that option, or else run "pnpm install" to recreate the modules directory.');
            }
        }
        catch (err) {
            if (!opts.forceNewNodeModules)
                throw err;
            await purgeModulesDirsOfImporter(rootProject);
            purged = true;
        }
    }
    await Promise.all(projects.map(async (project) => {
        try {
            checkCompatibility_1.default(modules, {
                modulesDir: project.modulesDir,
                storeDir: opts.storeDir,
                virtualStoreDir: opts.virtualStoreDir,
            });
            if (opts.lockfileDir !== project.rootDir && 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', `modules directory (at "${opts.lockfileDir}") was installed with ${stringifyIncludedDeps(modules.included)}. ` +
                            `Current install wants ${stringifyIncludedDeps(opts.include)}.`);
                    }
                }
            }
        }
        catch (err) {
            if (!opts.forceNewNodeModules)
                throw err;
            await purgeModulesDirsOfImporter(project);
            purged = true;
        }
    }));
    if (modules.registries && !R.equals(opts.registries, modules.registries)) {
        if (opts.forceNewNodeModules) {
            await Promise.all(projects.map(purgeModulesDirsOfImporter));
            return { purged: true };
        }
        throw new error_1.default('REGISTRIES_MISMATCH', `This modules directory was created using the following registries configuration: ${JSON.stringify(modules.registries)}. The current configuration is ${JSON.stringify(opts.registries)}. To recreate recreate the modules directory using the new settings, run "pnpm install".`);
    }
    if (purged && !rootProject) {
        await purgeModulesDirsOfImporter({
            modulesDir: path.join(opts.lockfileDir, opts.modulesDir),
            rootDir: opts.lockfileDir,
        });
    }
    return { purged };
}
async function purgeModulesDirsOfImporter(importer) {
    logger_1.default.info({
        message: `Recreating ${importer.modulesDir}`,
        prefix: importer.rootDir,
    });
    try {
        await rimraf(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, alreadyPurged = false) {
    var _a, _b, _c, _d, _e;
    const { currentHoistPattern, hoistedAliases, projects, include, independentLeaves, modules, pendingBuilds, registries, shamefullyHoist, skipped, rootModulesDir, } = await read_projects_context_1.default([
        {
            rootDir: opts.dir,
        },
    ], {
        lockfileDir: opts.lockfileDir,
        modulesDir: opts.modulesDir,
    });
    const storeDir = opts.storeDir;
    const importer = projects[0];
    const modulesDir = importer.modulesDir;
    const importerId = importer.id;
    const virtualStoreDir = pathAbsolute((_a = opts.virtualStoreDir) !== null && _a !== void 0 ? _a : 'node_modules/.pnpm', opts.lockfileDir);
    if (modules && !alreadyPurged) {
        const { purged } = await validateNodeModules(modules, projects, {
            currentHoistPattern,
            forceNewNodeModules: opts.forceNewNodeModules === true,
            include: opts.include,
            lockfileDir: opts.lockfileDir,
            modulesDir: (_b = opts.modulesDir) !== null && _b !== void 0 ? _b : 'node_modules',
            registries: opts.registries,
            storeDir: opts.storeDir,
            virtualStoreDir,
            forceHoistPattern: opts.forceHoistPattern,
            hoistPattern: opts.hoistPattern,
            forceIndependentLeaves: opts.forceIndependentLeaves,
            independentLeaves: opts.independentLeaves,
            forceShamefullyHoist: opts.forceShamefullyHoist,
            shamefullyHoist: opts.shamefullyHoist,
        });
        if (purged) {
            return getContextForSingleImporter(manifest, opts, true);
        }
    }
    await makeDir(storeDir);
    const extraBinPaths = [
        ...opts.extraBinPaths || [],
    ];
    const sHoist = Boolean(typeof shamefullyHoist === 'undefined' ? opts.shamefullyHoist : shamefullyHoist);
    if (opts.hoistPattern && !sHoist) {
        extraBinPaths.unshift(path.join(virtualStoreDir, 'node_modules/.bin'));
    }
    const hoistedModulesDir = sHoist
        ? rootModulesDir : path.join(virtualStoreDir, 'node_modules');
    const ctx = {
        extraBinPaths,
        hoistedAliases,
        hoistedModulesDir,
        hoistPattern: opts.hoistPattern,
        importerId,
        include: opts.include || include,
        independentLeaves: Boolean(typeof independentLeaves === 'undefined' ? opts.independentLeaves : independentLeaves),
        lockfileDir: opts.lockfileDir,
        manifest: (_e = (_d = (_c = opts.hooks) === null || _c === void 0 ? void 0 : _c.readPackage) === null || _d === void 0 ? void 0 : _d.call(_c, manifest)) !== null && _e !== void 0 ? _e : manifest,
        modulesDir,
        modulesFile: modules,
        pendingBuilds,
        prefix: opts.dir,
        registries: {
            ...opts.registries,
            ...registries,
        },
        rootModulesDir,
        shamefullyHoist: sHoist,
        skipped,
        storeDir,
        virtualStoreDir,
        ...await readLockfiles_1.default({
            force: opts.force,
            forceSharedLockfile: opts.forceSharedLockfile,
            lockfileDir: opts.lockfileDir,
            projects: [{ id: importerId, rootDir: opts.dir }],
            registry: opts.registries.default,
            useLockfile: opts.useLockfile,
            virtualStoreDir,
        }),
    };
    core_loggers_1.packageManifestLogger.debug({
        initial: manifest,
        prefix: opts.dir,
    });
    return ctx;
}
exports.getContextForSingleImporter = getContextForSingleImporter;
//# sourceMappingURL=index.js.map