fba9a35075
Publish Plugins / Publish Plugins (push) Has been cancelled
- scripts/load-plugins.js: shared plugin loader for the novel CLI scripts - scripts/fetch-novel.js: download a novel via its plugin into a clean EPUB 3 - find-novel prints the plugin id/path for each match - kavita 1.0.1: innermost TOC entry names the chapter Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
69 lines
2.2 KiB
JavaScript
69 lines
2.2 KiB
JavaScript
// 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 });
|
|
}
|
|
}
|