"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getContextForSingleImporter = void 0;
const core_loggers_1 = require("@pnpm/core-loggers");
const error_1 = __importDefault(require("@pnpm/error"));
const logger_1 = __importDefault(require("@pnpm/logger"));
const read_projects_context_1 = __importDefault(require("@pnpm/read-projects-context"));
const types_1 = require("@pnpm/types");
const checkCompatibility_1 = __importDefault(require("./checkCompatibility"));
const readLockfiles_1 = __importDefault(require("./readLockfiles"));
const path = require("path");
const rimraf = require("@zkochan/rimraf");
const fs = require("mz/fs");
const pathAbsolute = require("path-absolute");
const R = require("ramda");
async function getContext(projects, opts) {
    var _a, _b, _c, _d, _e, _f, _g;
    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 validateModules(importersContext.modules, importersContext.projects, {
            currentHoistPattern: importersContext.currentHoistPattern,
            currentPublicHoistPattern: importersContext.currentPublicHoistPattern,
            forceNewModules: opts.forceNewModules === true,
            include: opts.include,
            lockfileDir: opts.lockfileDir,
            modulesDir,
            registries: opts.registries,
            storeDir: opts.storeDir,
            virtualStoreDir,
            forceHoistPattern: opts.forceHoistPattern,
            hoistPattern: opts.hoistPattern,
            forcePublicHoistPattern: opts.forcePublicHoistPattern,
            publicHoistPattern: opts.publicHoistPattern,
        });
        if (purged) {
            importersContext = await read_projects_context_1.default(projects, {
                lockfileDir: opts.lockfileDir,
                modulesDir,
            });
        }
    }
    await fs.mkdir(opts.storeDir, { recursive: true });
    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) {
        for (const project of importersContext.projects) {
            project.originalManifest = project.manifest;
            project.manifest = opts.hooks.readPackage(R.clone(project.manifest));
        }
    }
    const extraBinPaths = [
        ...opts.extraBinPaths || [],
    ];
    const hoistedModulesDir = path.join(virtualStoreDir, 'node_modules');
    if ((_d = opts.hoistPattern) === null || _d === void 0 ? void 0 : _d.length) {
        extraBinPaths.unshift(path.join(hoistedModulesDir, '.bin'));
    }
    const ctx = {
        extraBinPaths,
        hoistedDependencies: importersContext.hoistedDependencies,
        hoistedModulesDir,
        hoistPattern: (_e = importersContext.currentHoistPattern) !== null && _e !== void 0 ? _e : opts.hoistPattern,
        include: (_f = opts.include) !== null && _f !== void 0 ? _f : importersContext.include,
        lockfileDir: opts.lockfileDir,
        modulesFile: importersContext.modules,
        pendingBuilds: importersContext.pendingBuilds,
        projects: importersContext.projects,
        publicHoistPattern: (_g = importersContext.currentPublicHoistPattern) !== null && _g !== void 0 ? _g : opts.publicHoistPattern,
        registries: {
            ...opts.registries,
            ...importersContext.registries,
        },
        rootModulesDir: importersContext.rootModulesDir,
        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,
        }),
    };
    core_loggers_1.contextLogger.debug({
        currentLockfileExists: ctx.existsCurrentLockfile,
        storeDir: opts.storeDir,
        virtualStoreDir,
    });
    return ctx;
}
exports.default = getContext;
async function validateModules(modules, projects, opts) {
    const rootProject = projects.find(({ id }) => id === '.');
    if (opts.forcePublicHoistPattern &&
        // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
        !R.equals(modules.publicHoistPattern, opts.publicHoistPattern || undefined)) {
        if (opts.forceNewModules && rootProject) {
            await purgeModulesDirsOfImporter(opts.virtualStoreDir, rootProject);
            return { purged: true };
        }
        throw new error_1.default('PUBLIC_HOIST_PATTERN_DIFF', 'This modules directory was created using a different public-hoist-pattern value.' +
            ' Run "pnpm install" to recreate the modules directory.');
    }
    let purged = false;
    if (opts.forceHoistPattern && rootProject) {
        try {
            // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
            if (!R.equals(opts.currentHoistPattern, opts.hoistPattern || undefined)) {
                throw new error_1.default('HOIST_PATTERN_DIFF', 'This modules directory was created using a different hoist-pattern value.' +
                    ' Run "pnpm install" to recreate the modules directory.');
            }
        }
        catch (err) {
            if (!opts.forceNewModules)
                throw err;
            await purgeModulesDirsOfImporter(opts.virtualStoreDir, 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.forceNewModules)
                throw err;
            await purgeModulesDirsOfImporter(opts.virtualStoreDir, project);
            purged = true;
        }
    }));
    if (modules.registries && !R.equals(opts.registries, modules.registries)) {
        if (opts.forceNewModules) {
            await Promise.all(projects.map(purgeModulesDirsOfImporter.bind(null, opts.virtualStoreDir)));
            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 the modules directory using the new settings, run "pnpm install".`);
    }
    if (purged && !rootProject) {
        await purgeModulesDirsOfImporter(opts.virtualStoreDir, {
            modulesDir: path.join(opts.lockfileDir, opts.modulesDir),
            rootDir: opts.lockfileDir,
        });
    }
    return { purged };
}
async function purgeModulesDirsOfImporter(virtualStoreDir, importer) {
    logger_1.default.info({
        message: `Recreating ${importer.modulesDir}`,
        prefix: importer.rootDir,
    });
    try {
        // We don't remove the actual modules directory, just the contents of it.
        // 1. we will need the directory anyway.
        // 2. in some setups, pnpm won't even have permission to remove the modules directory.
        await removeContentsOfDir(importer.modulesDir, virtualStoreDir);
    }
    catch (err) {
        if (err.code !== 'ENOENT')
            throw err;
    }
}
async function removeContentsOfDir(dir, virtualStoreDir) {
    const items = await fs.readdir(dir);
    for (const item of items) {
        // The non-pnpm related hidden files are kept
        if (item.startsWith('.') &&
            item !== '.bin' &&
            item !== '.modules.yaml' &&
            !dirsAreEqual(path.join(dir, item), virtualStoreDir)) {
            continue;
        }
        await rimraf(path.join(dir, item));
    }
}
function dirsAreEqual(dir1, dir2) {
    return path.relative(dir1, dir2) === '';
}
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, _f, _g;
    const { currentHoistPattern, currentPublicHoistPattern, hoistedDependencies, projects, include, modules, pendingBuilds, registries, 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 validateModules(modules, projects, {
            currentHoistPattern,
            currentPublicHoistPattern,
            forceNewModules: opts.forceNewModules === 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,
            forcePublicHoistPattern: opts.forcePublicHoistPattern,
            publicHoistPattern: opts.publicHoistPattern,
        });
        if (purged) {
            return getContextForSingleImporter(manifest, opts, true);
        }
    }
    await fs.mkdir(storeDir, { recursive: true });
    const extraBinPaths = [
        ...opts.extraBinPaths || [],
    ];
    const hoistedModulesDir = path.join(virtualStoreDir, 'node_modules');
    if ((_c = opts.hoistPattern) === null || _c === void 0 ? void 0 : _c.length) {
        extraBinPaths.unshift(path.join(hoistedModulesDir, '.bin'));
    }
    const ctx = {
        extraBinPaths,
        hoistedDependencies,
        hoistedModulesDir,
        hoistPattern: currentHoistPattern !== null && currentHoistPattern !== void 0 ? currentHoistPattern : opts.hoistPattern,
        importerId,
        include: (_d = opts.include) !== null && _d !== void 0 ? _d : include,
        lockfileDir: opts.lockfileDir,
        manifest: (_g = (_f = (_e = opts.hooks) === null || _e === void 0 ? void 0 : _e.readPackage) === null || _f === void 0 ? void 0 : _f.call(_e, manifest)) !== null && _g !== void 0 ? _g : manifest,
        modulesDir,
        modulesFile: modules,
        pendingBuilds,
        prefix: opts.dir,
        publicHoistPattern: currentPublicHoistPattern !== null && currentPublicHoistPattern !== void 0 ? currentPublicHoistPattern : opts.publicHoistPattern,
        registries: {
            ...opts.registries,
            ...registries,
        },
        rootModulesDir,
        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,
    });
    core_loggers_1.contextLogger.debug({
        currentLockfileExists: ctx.existsCurrentLockfile,
        storeDir: opts.storeDir,
        virtualStoreDir,
    });
    return ctx;
}
exports.getContextForSingleImporter = getContextForSingleImporter;
//# sourceMappingURL=index.js.map