From e7bd464ff054f49d1bd0b224edce3e76e401373c Mon Sep 17 00:00:00 2001 From: SamTV12345 <40429738+samtv12345@users.noreply.github.com> Date: Mon, 25 May 2026 15:00:43 +0200 Subject: [PATCH] fix(plugin-compat): register .ts CJS extension handler for ep_markdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ep_markdown and similar plugins ship TypeScript source files and load them via CJS require('./exportMarkdown') without an extension. Node's CJS resolver does not recognise .ts by default. Register a Module._extensions handler at plugins.ts load time that uses esbuild's synchronous transformSync to compile .ts → CJS on demand. This shim is guard-checked so tsx/vite-node environments (which already handle .ts) are not affected. Co-Authored-By: Claude Sonnet 4.6 --- src/static/js/pluginfw/plugins.ts | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/static/js/pluginfw/plugins.ts b/src/static/js/pluginfw/plugins.ts index f68c47d4f..32a86fb3c 100644 --- a/src/static/js/pluginfw/plugins.ts +++ b/src/static/js/pluginfw/plugins.ts @@ -2,6 +2,8 @@ 'use strict'; import {pathToFileURL} from 'node:url'; import {promises as fs} from 'fs'; +import {Module} from 'node:module'; +import {readFileSync} from 'node:fs'; import log4js from 'log4js'; import path from 'path'; import runCmd from '../../../node/utils/run_cmd.js'; @@ -13,6 +15,34 @@ import settings, { getEpVersion, } from '../../../node/utils/Settings.js'; +// Register a .ts loader for CJS require() calls inside plugins. +// Some plugins (e.g. ep_markdown) ship TypeScript source files and load +// them via require('./someFile') without a .js extension. Node's CJS resolver +// does not understand .ts by default. We lazily compile .ts → CJS on first +// access using esbuild's synchronous transformSync API (esbuild is already a +// production dependency of ep_etherpad-lite). +// +// Vite-node / tsx handle this automatically in the test runner, but live plugin +// code goes through plain Node CJS resolution and needs this shim. +if (!(Module as any)._extensions['.ts']) { + (Module as any)._extensions['.ts'] = (mod: any, filename: string) => { + // Use a dynamic require for esbuild to avoid a hard circular-dependency + // at module-load time (esbuild is large). The call-time cost is negligible + // because Node caches require() results. + // eslint-disable-next-line @typescript-eslint/no-require-imports + const {transformSync} = require('esbuild'); + const source = readFileSync(filename, 'utf8'); + const {code} = transformSync(source, { + loader: 'ts', + format: 'cjs', + target: 'node24', + sourcemap: 'inline', + sourcefile: filename, + }); + mod._compile(code, filename); + }; +} + const logger = log4js.getLogger('plugins'); // Log the version of pnpm at startup. pnpm is only used for dev workflows