// 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 { FileSystem, InternalError, JsonFile, JsonSchema } from '@rushstack/node-core-library';
import { CommandLineConfiguration } from '../../api/CommandLineConfiguration';
import { RushConstants } from '../../logic/RushConstants';
import { RushSdk } from './RushSdk';
import schemaJson from '../../schemas/rush-plugin-manifest.schema.json';
export class PluginLoaderBase {
    constructor({ pluginConfiguration, rushConfiguration, terminal }) {
        this.packageName = pluginConfiguration.packageName;
        this.pluginName = pluginConfiguration.pluginName;
        this._rushConfiguration = rushConfiguration;
        this._terminal = terminal;
    }
    load() {
        const resolvedPluginPath = this._resolvePlugin();
        if (!resolvedPluginPath) {
            return undefined;
        }
        const pluginOptions = this._getPluginOptions();
        RushSdk.ensureInitialized();
        return this._loadAndValidatePluginPackage(resolvedPluginPath, pluginOptions);
    }
    get pluginManifest() {
        return this._getRushPluginManifest();
    }
    getCommandLineConfiguration() {
        const commandLineJsonFilePath = this._getCommandLineJsonFilePath();
        if (!commandLineJsonFilePath) {
            return undefined;
        }
        const commandLineConfiguration = CommandLineConfiguration.tryLoadFromFile(commandLineJsonFilePath);
        if (!commandLineConfiguration) {
            return undefined;
        }
        for (const additionalPathFolder of this._getCommandLineAdditionalPathFolders().reverse()) {
            commandLineConfiguration.prependAdditionalPathFolder(additionalPathFolder);
        }
        commandLineConfiguration.shellCommandTokenContext = {
            packageFolder: this.packageFolder
        };
        return commandLineConfiguration;
    }
    _getCommandLineAdditionalPathFolders() {
        return [
            // Example: `@microsoft/rush-lib/node_modules/<packageName>/node_modules/.bin`
            // Example: `common/autoinstaller/plugins/node_modules/<packageName>/node_modules/.bin`
            path.join(this.packageFolder, 'node_modules', '.bin')
        ];
    }
    _getCommandLineJsonFilePath() {
        const { commandLineJsonFilePath } = this._getRushPluginManifest();
        if (!commandLineJsonFilePath) {
            return undefined;
        }
        return path.join(this.packageFolder, commandLineJsonFilePath);
    }
    _loadAndValidatePluginPackage(resolvedPluginPath, options) {
        let pluginPackage;
        try {
            const loadedPluginPackage = require(resolvedPluginPath);
            pluginPackage = loadedPluginPackage.default || loadedPluginPackage;
        }
        catch (e) {
            throw new InternalError(`Error loading rush plugin from "${resolvedPluginPath}": ${e}`);
        }
        if (!pluginPackage) {
            throw new InternalError(`Rush plugin loaded from "${resolvedPluginPath}" is null or undefined.`);
        }
        this._terminal.writeVerboseLine(`Loaded rush plugin from "${resolvedPluginPath}"`);
        const plugin = new pluginPackage(options);
        if (!plugin.apply || typeof pluginPackage.apply !== 'function') {
            throw new InternalError(`Rush plugin must define an "apply" function. The plugin loaded from "${resolvedPluginPath}" ` +
                'either doesn\'t define an "apply" property, or its value isn\'t a function.');
        }
        return plugin;
    }
    _resolvePlugin() {
        const entryPoint = this._getRushPluginManifest().entryPoint;
        if (!entryPoint) {
            return undefined;
        }
        const packageFolder = this.packageFolder;
        const modulePath = path.join(packageFolder, entryPoint);
        if (!FileSystem.exists(modulePath)) {
            throw new InternalError(`Unable to find entry point "${modulePath}" for rush plugin "${this.pluginName}".`);
        }
        return modulePath;
    }
    _getPluginOptions() {
        const optionsJsonFilePath = this._getPluginOptionsJsonFilePath();
        const optionsSchema = this._getRushPluginOptionsSchema();
        let pluginOptions = {};
        try {
            pluginOptions = JsonFile.load(optionsJsonFilePath);
        }
        catch (e) {
            if (FileSystem.isFileDoesNotExistError(e)) {
                return {};
            }
            throw e;
        }
        if (optionsSchema) {
            optionsSchema.validateObject(pluginOptions, optionsJsonFilePath);
        }
        return pluginOptions;
    }
    _getPluginOptionsJsonFilePath() {
        return path.join(this._rushConfiguration.rushPluginOptionsFolder, `${this.pluginName}.json`);
    }
    _getRushPluginOptionsSchema() {
        const optionsSchema = this._getRushPluginManifest().optionsSchema;
        if (!optionsSchema) {
            return undefined;
        }
        const optionsSchemaFilePath = path.join(this.packageFolder, optionsSchema);
        return JsonSchema.fromFile(optionsSchemaFilePath);
    }
    _getRushPluginManifest() {
        if (!this._manifestCache) {
            const packageName = this.packageName;
            const pluginName = this.pluginName;
            const manifestPath = this._getManifestPath();
            if (!FileSystem.exists(manifestPath)) {
                throw new Error(`Manifest for rush plugin package ${packageName} not found.\nPlease run 'rush update' first.`);
            }
            const rushPluginManifestJson = JsonFile.loadAndValidate(manifestPath, PluginLoaderBase._jsonSchema);
            const pluginManifest = rushPluginManifestJson.plugins.find((item) => item.pluginName === pluginName);
            if (!pluginManifest) {
                throw new Error(`${pluginName} is not provided by Rush plugin package "${packageName}"`);
            }
            this._manifestCache = pluginManifest;
        }
        return this._manifestCache;
    }
    _getManifestPath() {
        return path.join(this.packageFolder, RushConstants.rushPluginManifestFilename);
    }
}
PluginLoaderBase._jsonSchema = JsonSchema.fromLoadedObject(schemaJson);
//# sourceMappingURL=PluginLoaderBase.js.map