// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
import * as path from 'node:path';
import { CommandLineAction } from '@rushstack/ts-command-line';
import { LockFile } from '@rushstack/node-core-library';
import { Colorize } from '@rushstack/terminal';
import { EventHooksManager } from '../../logic/EventHooksManager';
import { RushCommandLineParser } from '../RushCommandLineParser';
import { Utilities } from '../../utilities/Utilities';
import { measureAsyncFn } from '../../utilities/performance';
const PERF_PREFIX = 'rush:action';
/**
 * The base class for a few specialized Rush command-line actions that
 * can be used without a rush.json configuration.
 */
export class BaseConfiglessRushAction extends CommandLineAction {
    constructor(options) {
        super(options);
        const { parser, safeForSimultaneousRushProcesses } = options;
        this.parser = parser;
        const { rushConfiguration, terminal, rushSession, rushGlobalFolder } = parser;
        this._safeForSimultaneousRushProcesses = !!safeForSimultaneousRushProcesses;
        this.rushConfiguration = rushConfiguration;
        this.terminal = terminal;
        this.rushSession = rushSession;
        this.rushGlobalFolder = rushGlobalFolder;
    }
    async onExecuteAsync() {
        this._ensureEnvironment();
        if (this.rushConfiguration) {
            if (!this._safeForSimultaneousRushProcesses) {
                if (!LockFile.tryAcquire(this.rushConfiguration.commonTempFolder, 'rush')) {
                    this.terminal.writeLine(Colorize.red(`Another Rush command is already running in this repository.`));
                    process.exit(1);
                }
            }
        }
        if (!RushCommandLineParser.shouldRestrictConsoleOutput()) {
            this.terminal.write(`Starting "rush ${this.actionName}"\n`);
        }
        await measureAsyncFn(`${PERF_PREFIX}:runAsync`, () => this.runAsync());
    }
    _ensureEnvironment() {
        if (this.rushConfiguration) {
            // eslint-disable-next-line dot-notation
            let environmentPath = process.env['PATH'];
            environmentPath =
                path.join(this.rushConfiguration.commonTempFolder, 'node_modules', '.bin') +
                    path.delimiter +
                    environmentPath;
            // eslint-disable-next-line dot-notation
            process.env['PATH'] = environmentPath;
        }
    }
}
/**
 * The base class that most Rush command-line actions should extend.
 */
export class BaseRushAction extends BaseConfiglessRushAction {
    get eventHooksManager() {
        if (!this._eventHooksManager) {
            this._eventHooksManager = new EventHooksManager(this.rushConfiguration);
        }
        return this._eventHooksManager;
    }
    async onExecuteAsync() {
        if (!this.rushConfiguration) {
            throw Utilities.getRushConfigNotFoundError();
        }
        this._throwPluginErrorIfNeed();
        await measureAsyncFn(`${PERF_PREFIX}:initializePluginsAsync`, () => this.parser.pluginManager.tryInitializeAssociatedCommandPluginsAsync(this.actionName));
        this._throwPluginErrorIfNeed();
        const { hooks: sessionHooks } = this.rushSession;
        await measureAsyncFn(`${PERF_PREFIX}:initializePlugins`, async () => {
            if (sessionHooks.initialize.isUsed()) {
                // Avoid the cost of compiling the hook if it wasn't tapped.
                await sessionHooks.initialize.promise(this);
            }
        });
        return super.onExecuteAsync();
    }
    /**
     * If an error is encountered while trying to load plugins, it is saved in the `PluginManager.error`
     * property, so we can defer throwing it until when `_throwPluginErrorIfNeed()` is called.
     */
    _throwPluginErrorIfNeed() {
        // If the plugin configuration is broken, these three commands are used to fix the problem:
        //
        //   "rush update"
        //   "rush init-autoinstaller"
        //   "rush update-autoinstaller"
        //
        // In addition, the "rush setup" command is designed to help new users configure their access
        // to a private NPM registry, which means it can't rely on plugins that might live in that
        // registry.
        //
        // Thus we do not report plugin errors when invoking these commands.
        if (!['update', 'init-autoinstaller', 'update-autoinstaller', 'setup'].includes(this.actionName)) {
            const pluginError = this.parser.pluginManager.error;
            if (pluginError) {
                throw pluginError;
            }
        }
    }
}
//# sourceMappingURL=BaseRushAction.js.map