// Load compiled plugin instances outside the app, for the novel CLI scripts. import * as esbuild from 'esbuild'; import { execFileSync } from 'child_process'; import { createRequire } from 'module'; import { fileURLToPath } from 'url'; import path, { dirname } from 'path'; import fs from 'fs'; import os from 'os'; const require = createRequire(import.meta.url); export const ROOT = path.resolve(dirname(fileURLToPath(import.meta.url)), '..'); // The shared fetch helper logs every request with its headers (credentials // included). Callers print their output explicitly with `print`. export const print = (...args) => process.stdout.write(args.join(' ') + '\n'); console.log = () => undefined; /** Bundle and instantiate every plugin in the given language folders. */ export async function loadPlugins(langs) { // Multisrc plugins (Madara, NovelFire, ...) are generated, not committed. execFileSync('node', ['plugins/multisrc/generate-multisrc-plugins.js'], { cwd: ROOT, stdio: 'ignore', }); const entryPoints = langs.flatMap(lang => { const dir = path.join(ROOT, 'plugins', lang); if (!fs.existsSync(dir)) throw new Error(`No plugin folder: plugins/${lang}`); return fs .readdirSync(dir) .filter(f => f.endsWith('.ts')) .map(f => path.join(dir, f)); }); const outdir = fs.mkdtempSync(path.join(os.tmpdir(), 'novel-plugins-')); try { const result = await esbuild.build({ entryPoints, outdir, bundle: true, platform: 'node', format: 'cjs', target: 'node22', outExtension: { '.js': '.cjs' }, logLevel: 'silent', alias: { '@libs': path.join(ROOT, 'src/libs'), '@': path.join(ROOT, 'src'), }, }); if (result.errors.length) throw new Error('Plugin bundling failed'); const plugins = []; for (const file of fs.readdirSync(outdir, { recursive: true })) { if (!file.endsWith('.cjs')) continue; try { const mod = require(path.join(outdir, file)); plugins.push(mod.default ?? mod); } catch { // A plugin that throws on load is skipped, like a broken source in the app. } } return plugins; } finally { fs.rmSync(outdir, { recursive: true, force: true }); } }