"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.InstallHelpers = void 0;
const path = __importStar(require("node:path"));
const semver = __importStar(require("semver"));
const node_core_library_1 = require("@rushstack/node-core-library");
const terminal_1 = require("@rushstack/terminal");
const LastInstallFlag_1 = require("../../api/LastInstallFlag");
const Utilities_1 = require("../../utilities/Utilities");
const objectUtilities_1 = require("../../utilities/objectUtilities");
const RushConstants_1 = require("../RushConstants");
class InstallHelpers {
    static generateCommonPackageJson(rushConfiguration, subspace, dependencies = new Map(), terminal) {
        const commonPackageJson = {
            dependencies: {},
            description: 'Temporary file generated by the Rush tool',
            name: 'rush-common',
            private: true,
            version: '0.0.0'
        };
        if (rushConfiguration.isPnpm) {
            const pnpmOptions = subspace.getPnpmOptions() || rushConfiguration.pnpmOptions;
            if (!commonPackageJson.pnpm) {
                commonPackageJson.pnpm = {};
            }
            if (pnpmOptions.globalOverrides) {
                commonPackageJson.pnpm.overrides = pnpmOptions.globalOverrides;
            }
            if (pnpmOptions.globalPackageExtensions) {
                commonPackageJson.pnpm.packageExtensions = pnpmOptions.globalPackageExtensions;
            }
            if (pnpmOptions.globalPeerDependencyRules) {
                commonPackageJson.pnpm.peerDependencyRules = pnpmOptions.globalPeerDependencyRules;
            }
            if (pnpmOptions.globalNeverBuiltDependencies) {
                commonPackageJson.pnpm.neverBuiltDependencies = pnpmOptions.globalNeverBuiltDependencies;
            }
            if (pnpmOptions.globalOnlyBuiltDependencies) {
                if (rushConfiguration.rushConfigurationJson.pnpmVersion !== undefined &&
                    semver.lt(rushConfiguration.rushConfigurationJson.pnpmVersion, '10.1.0')) {
                    terminal.writeWarningLine(terminal_1.Colorize.yellow(`Your version of pnpm (${rushConfiguration.rushConfigurationJson.pnpmVersion}) ` +
                        `doesn't support the "globalOnlyBuiltDependencies" field in ` +
                        `${rushConfiguration.commonRushConfigFolder}/${RushConstants_1.RushConstants.pnpmConfigFilename}. ` +
                        'Remove this field or upgrade to pnpm 10.1.0 or newer.'));
                }
                commonPackageJson.pnpm.onlyBuiltDependencies = pnpmOptions.globalOnlyBuiltDependencies;
            }
            if (pnpmOptions.globalIgnoredOptionalDependencies) {
                if (rushConfiguration.rushConfigurationJson.pnpmVersion !== undefined &&
                    semver.lt(rushConfiguration.rushConfigurationJson.pnpmVersion, '9.0.0')) {
                    terminal.writeWarningLine(terminal_1.Colorize.yellow(`Your version of pnpm (${rushConfiguration.rushConfigurationJson.pnpmVersion}) ` +
                        `doesn't support the "globalIgnoredOptionalDependencies" field in ` +
                        `${rushConfiguration.commonRushConfigFolder}/${RushConstants_1.RushConstants.pnpmConfigFilename}. ` +
                        'Remove this field or upgrade to pnpm 9.'));
                }
                commonPackageJson.pnpm.ignoredOptionalDependencies = pnpmOptions.globalIgnoredOptionalDependencies;
            }
            if (pnpmOptions.globalAllowedDeprecatedVersions) {
                commonPackageJson.pnpm.allowedDeprecatedVersions = pnpmOptions.globalAllowedDeprecatedVersions;
            }
            if (pnpmOptions.globalPatchedDependencies) {
                commonPackageJson.pnpm.patchedDependencies = pnpmOptions.globalPatchedDependencies;
            }
            if (pnpmOptions.minimumReleaseAge !== undefined || pnpmOptions.minimumReleaseAgeExclude) {
                if (rushConfiguration.rushConfigurationJson.pnpmVersion !== undefined &&
                    semver.lt(rushConfiguration.rushConfigurationJson.pnpmVersion, '10.16.0')) {
                    terminal.writeWarningLine(terminal_1.Colorize.yellow(`Your version of pnpm (${rushConfiguration.rushConfigurationJson.pnpmVersion}) ` +
                        `doesn't support the "minimumReleaseAge" or "minimumReleaseAgeExclude" fields in ` +
                        `${rushConfiguration.commonRushConfigFolder}/${RushConstants_1.RushConstants.pnpmConfigFilename}. ` +
                        'Remove these fields or upgrade to pnpm 10.16.0 or newer.'));
                }
                if (pnpmOptions.minimumReleaseAge !== undefined) {
                    commonPackageJson.pnpm.minimumReleaseAge = pnpmOptions.minimumReleaseAge;
                }
                if (pnpmOptions.minimumReleaseAgeExclude) {
                    commonPackageJson.pnpm.minimumReleaseAgeExclude = pnpmOptions.minimumReleaseAgeExclude;
                }
            }
            if (pnpmOptions.unsupportedPackageJsonSettings) {
                (0, objectUtilities_1.merge)(commonPackageJson, pnpmOptions.unsupportedPackageJsonSettings);
            }
        }
        // Add any preferred versions to the top of the commonPackageJson
        // do this in alphabetical order for simpler debugging
        for (const dependency of Array.from(dependencies.keys()).sort()) {
            commonPackageJson.dependencies[dependency] = dependencies.get(dependency);
        }
        // Example: "C:\MyRepo\common\temp\package.json"
        const commonPackageJsonFilename = path.join(subspace.getSubspaceTempFolderPath(), node_core_library_1.FileConstants.PackageJson);
        // Don't update the file timestamp unless the content has changed, since "rush install"
        // will consider this timestamp
        node_core_library_1.JsonFile.save(commonPackageJson, commonPackageJsonFilename, { onlyIfChanged: true });
    }
    static getPackageManagerEnvironment(rushConfiguration, options = {}) {
        let configurationEnvironment = undefined;
        if (rushConfiguration.packageManager === 'npm') {
            if (rushConfiguration.npmOptions && rushConfiguration.npmOptions.environmentVariables) {
                configurationEnvironment = rushConfiguration.npmOptions.environmentVariables;
            }
        }
        else if (rushConfiguration.isPnpm) {
            if (rushConfiguration.pnpmOptions && rushConfiguration.pnpmOptions.environmentVariables) {
                configurationEnvironment = rushConfiguration.pnpmOptions.environmentVariables;
            }
        }
        else if (rushConfiguration.packageManager === 'yarn') {
            if (rushConfiguration.yarnOptions && rushConfiguration.yarnOptions.environmentVariables) {
                configurationEnvironment = rushConfiguration.yarnOptions.environmentVariables;
            }
        }
        return InstallHelpers._mergeEnvironmentVariables(process.env, configurationEnvironment, options);
    }
    /**
     * If the "(p)npm-local" symlink hasn't been set up yet, this creates it, installing the
     * specified (P)npm version in the user's home directory if needed.
     */
    static async ensureLocalPackageManagerAsync(rushConfiguration, rushGlobalFolder, maxInstallAttempts, restrictConsoleOutput) {
        let logIfConsoleOutputIsNotRestricted;
        if (restrictConsoleOutput) {
            logIfConsoleOutputIsNotRestricted = () => {
                /* noop */
            };
        }
        else {
            logIfConsoleOutputIsNotRestricted = (message) => {
                // eslint-disable-next-line no-console
                console.log(message);
            };
        }
        // Example: "C:\Users\YourName\.rush"
        const rushUserFolder = rushGlobalFolder.nodeSpecificPath;
        if (!node_core_library_1.FileSystem.exists(rushUserFolder)) {
            logIfConsoleOutputIsNotRestricted('Creating ' + rushUserFolder);
            node_core_library_1.FileSystem.ensureFolder(rushUserFolder);
        }
        const packageManager = rushConfiguration.packageManager;
        const packageManagerVersion = rushConfiguration.packageManagerToolVersion;
        const packageManagerAndVersion = `${packageManager}-${packageManagerVersion}`;
        // Example: "C:\Users\YourName\.rush\pnpm-1.2.3"
        const packageManagerToolFolder = path.join(rushUserFolder, packageManagerAndVersion);
        const packageManagerMarker = new LastInstallFlag_1.LastInstallFlag(packageManagerToolFolder, {
            node: process.versions.node
        });
        logIfConsoleOutputIsNotRestricted(`Trying to acquire lock for ${packageManagerAndVersion}`);
        const lock = await node_core_library_1.LockFile.acquireAsync(rushUserFolder, packageManagerAndVersion);
        logIfConsoleOutputIsNotRestricted(`Acquired lock for ${packageManagerAndVersion}`);
        if (!(await packageManagerMarker.isValidAsync()) || lock.dirtyWhenAcquired) {
            logIfConsoleOutputIsNotRestricted(terminal_1.Colorize.bold(`Installing ${packageManager} version ${packageManagerVersion}\n`));
            // note that this will remove the last-install flag from the directory
            await Utilities_1.Utilities.installPackageInDirectoryAsync({
                directory: packageManagerToolFolder,
                packageName: packageManager,
                version: rushConfiguration.packageManagerToolVersion,
                tempPackageTitle: `${packageManager}-local-install`,
                maxInstallAttempts: maxInstallAttempts,
                // This is using a local configuration to install a package in a shared global location.
                // Generally that's a bad practice, but in this case if we can successfully install
                // the package at all, we can reasonably assume it's good for all the repositories.
                // In particular, we'll assume that two different NPM registries cannot have two
                // different implementations of the same version of the same package.
                // This was needed for: https://github.com/microsoft/rushstack/issues/691
                commonRushConfigFolder: rushConfiguration.commonRushConfigFolder,
                // Only filter npm-incompatible properties when the repo uses pnpm or yarn.
                // If the repo uses npm, the .npmrc is already configured for npm, so don't filter.
                filterNpmIncompatibleProperties: rushConfiguration.packageManager !== 'npm'
            });
            logIfConsoleOutputIsNotRestricted(`Successfully installed ${packageManager} version ${packageManagerVersion}`);
        }
        else {
            logIfConsoleOutputIsNotRestricted(`Found ${packageManager} version ${packageManagerVersion} in ${packageManagerToolFolder}`);
        }
        await packageManagerMarker.createAsync();
        // Example: "C:\MyRepo\common\temp"
        node_core_library_1.FileSystem.ensureFolder(rushConfiguration.commonTempFolder);
        // Example: "C:\MyRepo\common\temp\pnpm-local"
        const localPackageManagerToolFolder = path.join(rushConfiguration.commonTempFolder, `${packageManager}-local`);
        logIfConsoleOutputIsNotRestricted(`\nSymlinking "${localPackageManagerToolFolder}"`);
        logIfConsoleOutputIsNotRestricted(`  --> "${packageManagerToolFolder}"`);
        // We cannot use FileSystem.exists() to test the existence of a symlink, because it will
        // return false for broken symlinks.  There is no way to test without catching an exception.
        try {
            node_core_library_1.FileSystem.deleteFolder(localPackageManagerToolFolder);
        }
        catch (error) {
            if (error.code !== 'ENOENT') {
                throw error;
            }
        }
        node_core_library_1.FileSystem.createSymbolicLinkJunction({
            linkTargetPath: packageManagerToolFolder,
            newLinkPath: localPackageManagerToolFolder
        });
        lock.release();
    }
    // Helper for getPackageManagerEnvironment
    static _mergeEnvironmentVariables(baseEnv, environmentVariables, options = {}) {
        const packageManagerEnv = baseEnv;
        if (environmentVariables) {
            // eslint-disable-next-line guard-for-in
            for (const envVar in environmentVariables) {
                let setEnvironmentVariable = true;
                // eslint-disable-next-line no-console
                console.log(`\nProcessing definition for environment variable: ${envVar}`);
                if (baseEnv.hasOwnProperty(envVar)) {
                    setEnvironmentVariable = false;
                    // eslint-disable-next-line no-console
                    console.log(`Environment variable already defined:`);
                    // eslint-disable-next-line no-console
                    console.log(`  Name: ${envVar}`);
                    // eslint-disable-next-line no-console
                    console.log(`  Existing value: ${baseEnv[envVar]}`);
                    // eslint-disable-next-line no-console
                    console.log(`  Value set in ${RushConstants_1.RushConstants.rushJsonFilename}: ${environmentVariables[envVar].value}`);
                    if (environmentVariables[envVar].override) {
                        setEnvironmentVariable = true;
                        // eslint-disable-next-line no-console
                        console.log(`Overriding the environment variable with the value set in ${RushConstants_1.RushConstants.rushJsonFilename}.`);
                    }
                    else {
                        // eslint-disable-next-line no-console
                        console.log(terminal_1.Colorize.yellow(`WARNING: Not overriding the value of the environment variable.`));
                    }
                }
                if (setEnvironmentVariable) {
                    if (options.debug) {
                        // eslint-disable-next-line no-console
                        console.log(`Setting environment variable for package manager.`);
                        // eslint-disable-next-line no-console
                        console.log(`  Name: ${envVar}`);
                        // eslint-disable-next-line no-console
                        console.log(`  Value: ${environmentVariables[envVar].value}`);
                    }
                    packageManagerEnv[envVar] = environmentVariables[envVar].value;
                }
            }
        }
        return packageManagerEnv;
    }
}
exports.InstallHelpers = InstallHelpers;
//# sourceMappingURL=InstallHelpers.js.map