Support ESM and CJS plugins via data URL import fallback
This commit is contained in:
@@ -16,6 +16,33 @@
|
|||||||
);
|
);
|
||||||
const { PluginRegistry, createPluginContext } = await import(pathToFileURL(pmEntry).href);
|
const { PluginRegistry, createPluginContext } = await import(pathToFileURL(pmEntry).href);
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Module loader — supports both CJS (module.exports) and ESM (export default)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates plugin source code and returns the plugin object.
|
||||||
|
* Tries CJS eval first; falls back to ESM data-URL import for ES module syntax.
|
||||||
|
* @param {string} code
|
||||||
|
* @returns {Promise<object>}
|
||||||
|
*/
|
||||||
|
async function evalPluginCode(code) {
|
||||||
|
const isESM = /^\s*(import\s|export\s)/m.test(code);
|
||||||
|
|
||||||
|
if (!isESM) {
|
||||||
|
// CommonJS path
|
||||||
|
const m = { exports: {} };
|
||||||
|
// eslint-disable-next-line no-new-func
|
||||||
|
new Function('module', 'exports', code)(m, m.exports);
|
||||||
|
return m.exports?.default ?? m.exports;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ESM path — import via data URL so the JS engine handles the module syntax
|
||||||
|
const dataUrl = `data:text/javascript;charset=utf-8,${encodeURIComponent(code)}`;
|
||||||
|
const mod = await import(dataUrl);
|
||||||
|
return mod.default ?? mod;
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Logging helper — writes to the on-screen log panel
|
// Logging helper — writes to the on-screen log panel
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -53,11 +80,7 @@
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
const pluginPath = path.join(__dirname, 'plugins', 'example-plugin', 'index.js');
|
const pluginPath = path.join(__dirname, 'plugins', 'example-plugin', 'index.js');
|
||||||
const pluginCode = fs.readFileSync(pluginPath, 'utf-8');
|
const pluginCode = fs.readFileSync(pluginPath, 'utf-8');
|
||||||
const pluginExports = {};
|
const plugin = await evalPluginCode(pluginCode);
|
||||||
const mod = { exports: pluginExports };
|
|
||||||
// eslint-disable-next-line no-new-func
|
|
||||||
new Function('module', 'exports', pluginCode)(mod, pluginExports);
|
|
||||||
const plugin = mod.exports;
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Simple in-process event emitter (stands in for a real client like Matrix)
|
// Simple in-process event emitter (stands in for a real client like Matrix)
|
||||||
@@ -209,10 +232,7 @@
|
|||||||
async function loadPluginFromPath(filePath) {
|
async function loadPluginFromPath(filePath) {
|
||||||
try {
|
try {
|
||||||
const code = fs.readFileSync(filePath, 'utf-8');
|
const code = fs.readFileSync(filePath, 'utf-8');
|
||||||
const m = { exports: {} };
|
const p = await evalPluginCode(code);
|
||||||
// eslint-disable-next-line no-new-func
|
|
||||||
new Function('module', 'exports', code)(m, m.exports);
|
|
||||||
const p = m.exports;
|
|
||||||
|
|
||||||
if (!p || typeof p.onLoad !== 'function') {
|
if (!p || typeof p.onLoad !== 'function') {
|
||||||
throw new Error('File does not export a valid plugin (needs an onLoad function)');
|
throw new Error('File does not export a valid plugin (needs an onLoad function)');
|
||||||
@@ -348,10 +368,7 @@
|
|||||||
if (!res.ok) throw new Error(`HTTP ${res.status} fetching ${downloadUrl}`);
|
if (!res.ok) throw new Error(`HTTP ${res.status} fetching ${downloadUrl}`);
|
||||||
const code = await res.text();
|
const code = await res.text();
|
||||||
|
|
||||||
const m = { exports: {} };
|
const p = await evalPluginCode(code);
|
||||||
// eslint-disable-next-line no-new-func
|
|
||||||
new Function('module', 'exports', code)(m, m.exports);
|
|
||||||
const p = m.exports;
|
|
||||||
|
|
||||||
if (!p || typeof p.onLoad !== 'function') {
|
if (!p || typeof p.onLoad !== 'function') {
|
||||||
throw new Error(`Plugin ${pluginId} does not export a valid plugin object`);
|
throw new Error(`Plugin ${pluginId} does not export a valid plugin object`);
|
||||||
|
|||||||
Reference in New Issue
Block a user