IMokaPlugin instances. Plugins are resolved from the DI container and matched against declarations in the site configuration." /> IMokaPlugin instances. Plugins are resolved from the DI container and matched against declarations in the site configuration." /> IMokaPlugin instances. Plugins are resolved from the DI container and matched against declarations in the site configuration." />
Class Sealed
public sealed class PluginHost

Namespace: Moka.Docs.Plugins

Discovers, loads, and manages the lifecycle of IMokaPlugin instances. Plugins are resolved from the DI container and matched against declarations in the site configuration.

Constructors

NameDescription
PluginHost(…) Creates a new plugin host.

PluginHost(SiteConfig siteConfig, IServiceProvider serviceProvider, PluginHost> logger)

PluginHost.PluginHost(SiteConfig siteConfig, IServiceProvider serviceProvider, ILogger<PluginHost> logger)

Creates a new plugin host.

Parameters

NameTypeDescription
siteConfigSiteConfigThe site configuration containing plugin declarations.
serviceProviderIServiceProviderThe host service provider.
loggerILogger<Moka.Docs.Plugins.PluginHost>Logger instance.

Properties

NameDescription
LoadedPlugins The currently loaded plugins.

LoadedPlugins

IReadOnlyList<LoadedPlugin> PluginHost.LoadedPlugins { get; }

The currently loaded plugins.

Methods

NameDescription
DiscoverAndInitializeAsync(CancellationToken ct) Discovers and initializes all plugins declared in the site configuration. Plugins registered in the DI container via IMokaPlugin are matched by their IMokaPlugin.Id against PluginDeclaration.Name.
ExecuteAllAsync(BuildContext buildContext, CancellationToken ct) Executes all loaded plugins against the given build context.

DiscoverAndInitializeAsync(CancellationToken ct)

Task PluginHost.DiscoverAndInitializeAsync(CancellationToken ct = null)

Discovers and initializes all plugins declared in the site configuration. Plugins registered in the DI container via IMokaPlugin are matched by their IMokaPlugin.Id against PluginDeclaration.Name.

Parameters

NameTypeDescription
ctCancellationTokenCancellation token.

ExecuteAllAsync(BuildContext buildContext, CancellationToken ct)

Task PluginHost.ExecuteAllAsync(BuildContext buildContext, CancellationToken ct = null)

Executes all loaded plugins against the given build context.

Parameters

NameTypeDescription
buildContextBuildContextThe current build context.
ctCancellationTokenCancellation token.
View Source
/// <summary>
///     Discovers, loads, and manages the lifecycle of <see cref = "IMokaPlugin"/> instances.
///     Plugins are resolved from the DI container and matched against declarations
///     in the site configuration.
/// </summary>
public sealed class PluginHost
{
    private readonly List<LoadedPlugin> _loaded = [];
    private readonly ILogger<PluginHost> _logger;
    private readonly IServiceProvider _serviceProvider;
    private readonly SiteConfig _siteConfig;
    /// <summary>
    ///     Creates a new plugin host.
    /// </summary>
    /// <param name = "siteConfig">The site configuration containing plugin declarations.</param>
    /// <param name = "serviceProvider">The host service provider.</param>
    /// <param name = "logger">Logger instance.</param>
    public PluginHost(SiteConfig siteConfig, IServiceProvider serviceProvider, ILogger<PluginHost> logger)
    {
        _siteConfig = siteConfig;
        _serviceProvider = serviceProvider;
        _logger = logger;
    }

    /// <summary>
    ///     The currently loaded plugins.
    /// </summary>
    public IReadOnlyList<LoadedPlugin> LoadedPlugins => _loaded;

    /// <summary>
    ///     Discovers and initializes all plugins declared in the site configuration.
    ///     Plugins registered in the DI container via <see cref = "IMokaPlugin"/> are matched
    ///     by their <see cref = "IMokaPlugin.Id"/> against <see cref = "PluginDeclaration.Name"/>.
    /// </summary>
    /// <param name = "ct">Cancellation token.</param>
    public async Task DiscoverAndInitializeAsync(CancellationToken ct = default)
    {
        List<PluginDeclaration> declarations = _siteConfig.Plugins;
        if (declarations.Count == 0)
        {
            _logger.LogDebug("No plugins declared in configuration");
            return;
        }

        // Resolve all registered IMokaPlugin instances from DI
        IEnumerable<IMokaPlugin> available = _serviceProvider.GetService(typeof(IEnumerable<IMokaPlugin>)) as IEnumerable<IMokaPlugin> ?? [];
        var pluginMap = new Dictionary<string, IMokaPlugin>(StringComparer.OrdinalIgnoreCase);
        foreach (IMokaPlugin plugin in available)
        {
            pluginMap[plugin.Id] = plugin;
        }

        foreach (PluginDeclaration declaration in declarations)
        {
            if (string.IsNullOrWhiteSpace(declaration.Name))
            {
                _logger.LogWarning("Skipping plugin declaration with no name");
                continue;
            }

            if (!pluginMap.TryGetValue(declaration.Name, out IMokaPlugin? plugin))
            {
                _logger.LogWarning("Plugin '{PluginName}' declared in config but not found in DI container", declaration.Name);
                continue;
            }

            ILogger pluginLogger = _serviceProvider.GetService(typeof(ILogger<PluginHost>)) as ILogger ?? _logger;
            var context = new PluginContext(_siteConfig, declaration.Options.AsReadOnly(), _serviceProvider, pluginLogger);
            try
            {
                await plugin.InitializeAsync(context, ct);
                _loaded.Add(new LoadedPlugin(plugin, context, declaration));
                _logger.LogInformation("Loaded plugin '{PluginName}' v{Version}", plugin.Name, plugin.Version);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to initialize plugin '{PluginName}'", plugin.Name);
            }
        }
    }

    /// <summary>
    ///     Executes all loaded plugins against the given build context.
    /// </summary>
    /// <param name = "buildContext">The current build context.</param>
    /// <param name = "ct">Cancellation token.</param>
    public async Task ExecuteAllAsync(BuildContext buildContext, CancellationToken ct = default)
    {
        foreach (LoadedPlugin loaded in _loaded)
        {
            try
            {
                _logger.LogDebug("Executing plugin '{PluginName}'", loaded.Plugin.Name);
                await loaded.Plugin.ExecuteAsync(loaded.Context, buildContext, ct);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Plugin '{PluginName}' failed during execution", loaded.Plugin.Name);
            }
        }
    }
}
Was this page helpful?