@villedemontreal/workit-core
Version:
This package provides default and no-op implementations of the WorkIt types for client packages.
126 lines • 5.5 kB
JavaScript
;
/*
* Copyright (c) 2025 Ville de Montreal. All rights reserved.
* Licensed under the MIT license.
* See LICENSE file in the project root for full license information.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.searchPathForTest = exports.PluginLoader = void 0;
/* eslint @typescript-eslint/no-unsafe-assignment: 0 */
/* eslint @typescript-eslint/no-unsafe-call: 0 */
/* eslint @typescript-eslint/no-unsafe-member-access: 0 */
/* eslint @typescript-eslint/no-var-requires: 0 */
/* eslint @typescript-eslint/no-unsafe-return: 0 */
/* eslint @typescript-eslint/explicit-member-accessibility: 0 */
/* eslint @typescript-eslint/restrict-template-expressions: 0 */
/* eslint import/no-dynamic-require: 0 */
/* eslint global-require: 0 */
/* eslint no-restricted-syntax: 0 */
const workit_types_1 = require("@villedemontreal/workit-types");
/**
* Returns the Plugins object that meet the below conditions.
* Valid criteria: 1. It should be enabled. 2. Should have non-empty path.
*/
function filterPlugins(plugins) {
const keys = Object.keys(plugins);
return keys.reduce((acc, key) => {
if (plugins[key].enabled && plugins[key].path)
acc[key] = plugins[key];
return acc;
}, {});
}
/**
* The PluginLoader class can load instrumentation plugins that use a patch
* mechanism to enable automatic tracing for specific target modules.
*/
class PluginLoader {
/** Constructs a new PluginLoader instance. */
constructor(ioc, logger) {
this.ioc = ioc;
this.logger = logger;
/** A list of loaded plugins. */
this._plugins = [];
/**
* A field that tracks whether the plugin has been loaded
* for the first time, as well as whether the plugin is activated or not.
*/
this._hookState = workit_types_1.HookState.UNINITIALIZED;
}
/**
* Loads a list of plugins. Each plugin module should implement the core
* {@link Plugin} interface and export an instance named as 'plugin'.
* @param Plugins an object whose keys are plugin names and whose
* {@link PluginConfig} values indicate several configuration options.
*/
load(plugins) {
if (this._hookState === workit_types_1.HookState.UNINITIALIZED) {
const pluginsToLoad = filterPlugins(plugins);
const modulesToHook = Object.keys(pluginsToLoad);
if (modulesToHook.length === 0) {
this._hookState = workit_types_1.HookState.UNLOADED;
return;
}
const alreadyRequiredModules = Object.keys(require.cache);
const requiredModulesToHook = modulesToHook.filter((name) => alreadyRequiredModules.find((cached) => {
try {
return require.resolve(name) === cached;
}
catch (err) {
return false;
}
}) !== undefined);
if (requiredModulesToHook.length > 0) {
this.logger.info(`Some modules (${requiredModulesToHook.join(', ')}) were already required when their respective plugin was loaded, some plugins might not work. Make sure Workit is setup before you require in other modules.`);
}
modulesToHook.forEach((name) => {
const config = pluginsToLoad[name];
const modulePath = config.path;
const version = null;
this.logger.info(`PluginLoader#load: trying loading ${name}@${version}`);
this.logger.debug(`PluginLoader#load: applying binding to ${name}@${version} using ${modulePath} module`);
// Expecting a plugin from module;
try {
const { plugin } = require(modulePath);
if (plugin.moduleName !== name) {
this.logger.error(`PluginLoader#load: Entry ${name} use a plugin that instruments ${plugin.moduleName}`);
return exports;
}
this._plugins.push(plugin);
// Enable each supported plugin.
return plugin.enable(this.ioc, this.logger, config);
}
catch (e) {
this.logger.error(`PluginLoader#load: could not load plugin ${modulePath} of module ${name}. Error: ${e.message}`);
return exports;
}
});
this._hookState = workit_types_1.HookState.LOADED;
}
else if (this._hookState === workit_types_1.HookState.UNLOADED) {
this.logger.error('PluginLoader#load: Currently cannot re-enable plugin loader.');
}
else {
this.logger.error('PluginLoader#load: Plugin loader already enabled.');
}
}
/** Unloads plugins. */
unload() {
if (this._hookState === workit_types_1.HookState.LOADED) {
for (const plugin of this._plugins) {
plugin.disable();
}
this._plugins = [];
this._hookState = workit_types_1.HookState.UNLOADED;
}
}
}
exports.PluginLoader = PluginLoader;
/**
* Adds a search path for plugin modules. Intended for testing purposes only.
* @param searchPath The path to add.
*/
function searchPathForTest(searchPath) {
module.paths.push(searchPath);
}
exports.searchPathForTest = searchPathForTest;
//# sourceMappingURL=pluginLoader.js.map