@electron-forge/core
Version:
A complete tool for building modern Electron applications
161 lines (143 loc) • 5.61 kB
text/typescript
import { PluginBase } from '@electron-forge/plugin-base';
import {
ForgeListrTaskDefinition,
ForgeMutatingHookFn,
ForgeMutatingHookSignatures,
ForgeSimpleHookFn,
ForgeSimpleHookSignatures,
IForgePlugin,
IForgePluginInterface,
ResolvedForgeConfig,
StartResult,
} from '@electron-forge/shared-types';
import chalk from 'chalk';
import debug from 'debug';
import { StartOptions } from '../api';
import requireSearch from './require-search';
const d = debug('electron-forge:plugins');
function isForgePlugin(plugin: IForgePlugin | unknown): plugin is IForgePlugin {
return (plugin as IForgePlugin).__isElectronForgePlugin;
}
export default class PluginInterface implements IForgePluginInterface {
private plugins: IForgePlugin[];
private config: ResolvedForgeConfig;
constructor(dir: string, forgeConfig: ResolvedForgeConfig) {
this.plugins = forgeConfig.plugins.map((plugin) => {
if (isForgePlugin(plugin)) {
return plugin;
}
if (typeof plugin === 'object' && 'name' in plugin && 'config' in plugin) {
const { name: pluginName, config: opts } = plugin;
if (typeof pluginName !== 'string') {
throw new Error(`Expected plugin[0] to be a string but found ${pluginName}`);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const Plugin = requireSearch<any>(dir, [pluginName]);
if (!Plugin) {
throw new Error(`Could not find module with name: ${pluginName}. Make sure it's listed in the devDependencies of your package.json`);
}
return new Plugin(opts);
}
throw new Error(`Expected plugin to either be a plugin instance or a { name, config } object but found ${plugin}`);
});
// TODO: fix hack
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.config = null as any;
Object.defineProperty(this, 'config', {
value: forgeConfig,
enumerable: false,
configurable: false,
writable: false,
});
for (const plugin of this.plugins) {
plugin.init(dir, forgeConfig);
}
this.triggerHook = this.triggerHook.bind(this);
this.overrideStartLogic = this.overrideStartLogic.bind(this);
}
async triggerHook<Hook extends keyof ForgeSimpleHookSignatures>(hookName: Hook, hookArgs: ForgeSimpleHookSignatures[Hook]): Promise<void> {
for (const plugin of this.plugins) {
if (typeof plugin.getHooks === 'function') {
let hooks = plugin.getHooks()[hookName] as ForgeSimpleHookFn<Hook>[] | ForgeSimpleHookFn<Hook>;
if (hooks) {
if (typeof hooks === 'function') hooks = [hooks];
for (const hook of hooks) {
await hook(this.config, ...hookArgs);
}
}
}
}
}
async getHookListrTasks<Hook extends keyof ForgeSimpleHookSignatures>(
hookName: Hook,
hookArgs: ForgeSimpleHookSignatures[Hook]
): Promise<ForgeListrTaskDefinition[]> {
const tasks: ForgeListrTaskDefinition[] = [];
for (const plugin of this.plugins) {
if (typeof plugin.getHooks === 'function') {
let hooks = plugin.getHooks()[hookName] as ForgeSimpleHookFn<Hook>[] | ForgeSimpleHookFn<Hook>;
if (hooks) {
if (typeof hooks === 'function') hooks = [hooks];
for (const hook of hooks) {
tasks.push({
title: `${chalk.cyan(`[plugin-${plugin.name}]`)} ${(hook as any).__hookName || `Running ${chalk.yellow(hookName)} hook`}`,
task: async (_, task) => {
if ((hook as any).__hookName) {
// Also give it the task
await (hook as any).call(task, ...(hookArgs as any[]));
} else {
await hook(this.config, ...hookArgs);
}
},
options: {},
});
}
}
}
}
return tasks;
}
async triggerMutatingHook<Hook extends keyof ForgeMutatingHookSignatures>(
hookName: Hook,
...item: ForgeMutatingHookSignatures[Hook]
): Promise<ForgeMutatingHookSignatures[Hook][0]> {
let result: ForgeMutatingHookSignatures[Hook][0] = item[0];
for (const plugin of this.plugins) {
if (typeof plugin.getHooks === 'function') {
let hooks = plugin.getHooks()[hookName] as ForgeMutatingHookFn<Hook>[] | ForgeMutatingHookFn<Hook>;
if (hooks) {
if (typeof hooks === 'function') hooks = [hooks];
for (const hook of hooks) {
result = (await hook(this.config, ...item)) || result;
}
}
}
}
return result;
}
async overrideStartLogic(opts: StartOptions): Promise<StartResult> {
let newStartFn;
const claimed: string[] = [];
for (const plugin of this.plugins) {
if (typeof plugin.startLogic === 'function' && plugin.startLogic !== PluginBase.prototype.startLogic) {
claimed.push(plugin.name);
newStartFn = plugin.startLogic;
}
}
if (claimed.length > 1) {
throw new Error(`Multiple plugins tried to take control of the start command, please remove one of them\n --> ${claimed.join(', ')}`);
}
if (claimed.length === 1 && newStartFn) {
d(`plugin: "${claimed[0]}" has taken control of the start command`);
const result = await newStartFn(opts);
if (typeof result === 'object' && 'tasks' in result) {
result.tasks = result.tasks.map((task) => ({
...task,
title: `${chalk.cyan(`[plugin-${claimed[0]}]`)} ${task.title}`,
}));
}
return result;
}
return false;
}
}