mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-13 23:14:53 +09:00
Validate plugin entrypoint
Check that Master/Instance/Control plugins export the correct class and that this class is a subclass of the correct base class. This gives a better user experience when plugins are incorrectly set up and loads a duplicated clusterio/lib, and when accidentally using the wrong name when developing plugins.
This commit is contained in:
1 parent
6d7a2d46d6
commit
033de0e20a
2 files changed
+61
-6
No files matched your search
@@ -6,6 +6,7 @@
|
||||
"use strict";
|
||||
|
||||
const libErrors = require("./errors");
|
||||
const libPlugin = require("./plugin");
|
||||
const path = require("path");
|
||||
|
||||
|
||||
@@ -48,6 +49,22 @@ async function loadPluginInfos(pluginList) {
|
||||
return plugins;
|
||||
}
|
||||
|
||||
function loadPluginClass(entrypointName, className, pluginClass, pluginInfo) {
|
||||
let resolvedPath = path.posix.join(pluginInfo.requirePath, pluginInfo[entrypointName]);
|
||||
let entrypoint = require(resolvedPath);
|
||||
if (!entrypoint[className]) {
|
||||
throw new libErrors.PluginError(pluginInfo.name,
|
||||
new Error(`Expected ${resolvedPath} to export a class named ${className}`)
|
||||
);
|
||||
}
|
||||
if (!(entrypoint[className].prototype instanceof pluginClass)) {
|
||||
throw new libErrors.PluginError(pluginInfo.name,
|
||||
new Error(`Expected ${className} exported from ${resolvedPath} to be a subclass of ${pluginClass.name}`)
|
||||
);
|
||||
}
|
||||
return entrypoint[className];
|
||||
}
|
||||
|
||||
/**
|
||||
* Load master plugin class of a plugin
|
||||
*
|
||||
@@ -58,8 +75,7 @@ async function loadPluginInfos(pluginList) {
|
||||
* @static
|
||||
*/
|
||||
async function loadMasterPluginClass(pluginInfo) {
|
||||
let entrypoint = require(path.posix.join(pluginInfo.requirePath, pluginInfo.masterEntrypoint));
|
||||
return entrypoint.MasterPlugin;
|
||||
return loadPluginClass("masterEntrypoint", "MasterPlugin", libPlugin.BaseMasterPlugin, pluginInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,8 +88,7 @@ async function loadMasterPluginClass(pluginInfo) {
|
||||
* @static
|
||||
*/
|
||||
async function loadInstancePluginClass(pluginInfo) {
|
||||
let entrypoint = require(path.posix.join(pluginInfo.requirePath, pluginInfo.instanceEntrypoint));
|
||||
return entrypoint.InstancePlugin;
|
||||
return loadPluginClass("instanceEntrypoint", "InstancePlugin", libPlugin.BaseInstancePlugin, pluginInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,8 +101,7 @@ async function loadInstancePluginClass(pluginInfo) {
|
||||
* @static
|
||||
*/
|
||||
async function loadControlPluginClass(pluginInfo) {
|
||||
let entrypoint = require(path.posix.join(pluginInfo.requirePath, pluginInfo.controlEntrypoint));
|
||||
return entrypoint.ControlPlugin;
|
||||
return loadPluginClass("controlEntrypoint", "ControlPlugin", libPlugin.BaseControlPlugin, pluginInfo);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -64,4 +64,45 @@ describe("lib/plugin_loader", function() {
|
||||
);
|
||||
});
|
||||
});
|
||||
describe("loadMasterPluginClass()", function() {
|
||||
let baseDir = path.join("temp", "test", "plugin");
|
||||
let missingClass = path.join(baseDir, "missing_class_plugin");
|
||||
let wrongParentClass = path.join(baseDir, "wrong_parent_class_plugin");
|
||||
before(async function() {
|
||||
async function writeEntrypoint(pluginPath, content) {
|
||||
await fs.outputFile(path.join(pluginPath, "master.js"), content);
|
||||
}
|
||||
|
||||
await writeEntrypoint(missingClass, "");
|
||||
await writeEntrypoint(wrongParentClass, "class MasterPlugin {}\n module.exports = { MasterPlugin };\n");
|
||||
});
|
||||
it("should throw if class is missing from entrypoint", async function() {
|
||||
await assert.rejects(
|
||||
libPluginLoader.loadMasterPluginClass({
|
||||
name: "test",
|
||||
masterEntrypoint: "master",
|
||||
requirePath: path.resolve(missingClass),
|
||||
}),
|
||||
{
|
||||
message:
|
||||
`PluginError: Expected ${path.resolve(missingClass, "master")} ` +
|
||||
"to export a class named MasterPlugin",
|
||||
}
|
||||
);
|
||||
});
|
||||
it("should throw if class is not a subclass of BaseMasterPlugin", async function() {
|
||||
await assert.rejects(
|
||||
libPluginLoader.loadMasterPluginClass({
|
||||
name: "test",
|
||||
masterEntrypoint: "master",
|
||||
requirePath: path.resolve(wrongParentClass),
|
||||
}),
|
||||
{
|
||||
message:
|
||||
"PluginError: Expected MasterPlugin exported from " +
|
||||
`${path.resolve(wrongParentClass, "master")} to be a subclass of BaseMasterPlugin`,
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user