#13109: fix(plugins): inject globalThis.require for CJS interop in jiti-loaded extensions
size: S
trusted-contributor
experienced-contributor
Cluster:
Plugin Fixes and Enhancements
## Summary
Fixes #12854
Extensions loaded via jiti run in an ESM context where `require` is unavailable. CJS dependencies that internally call `require()` (e.g. `@vector-im/matrix-bot-sdk` calling `require("events")`) crash at runtime with `ReferenceError: require is not defined`.
This PR:
- Injects a `globalThis.require` shim at module load time via `createRequire(import.meta.url)` so CJS packages always have a usable `require`
- Re-anchors `globalThis.require` to each plugin's source path before `jiti()` evaluates it, so CJS dependencies resolve from the plugin's own `node_modules`
- Saves/restores the pre-existing `globalThis.require` around the plugin loading loop so runtime code outside the loader is unaffected
## Test plan
- [x] New test: `injects globalThis.require for CJS interop in jiti-loaded plugins` — verifies the shim exists and resolves built-in modules
- [x] New test: `loads plugins whose CJS dependencies call require() at runtime` — creates a real CJS helper that calls `require("node:path")`, loads a plugin that depends on it, verifies `status === "loaded"`
- [x] All 16 loader tests pass (TDD: both new tests fail before the fix, pass after)
- [x] `pnpm build && pnpm check` clean
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
This change adjusts the plugin loader so extensions evaluated via `jiti` (ESM context) have a `require` available for CommonJS interop. It injects a `globalThis.require` shim via `createRequire(import.meta.url)`, re-anchors `globalThis.require` to each plugin’s source path before `jiti()` evaluation so CJS dependencies resolve from the plugin’s own `node_modules`, and saves/restores any pre-existing `globalThis.require` around the plugin loading loop. Tests are added to verify the shim exists and can resolve built-in modules, and that plugins with CJS dependencies calling `require()` at runtime load successfully.
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge with minimal risk.
- The change is narrowly scoped to plugin loading, adds targeted tests that exercise the new behavior (globalThis.require injection and per-plugin require anchoring), and does not introduce broader behavioral changes beyond the loader’s evaluation context.
- No files require special attention
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#16019: fix(plugins): add postinstall patch for ESM-only package exports
by dashed · 2026-02-14
81.1%
#20402: Pr/load openclaw plugins async
by ramarnat · 2026-02-18
77.2%
#21660: fix(plugins): require explicit allowlist for non-bundled plugins
by AI-Reviewer-QS · 2026-02-20
75.8%
#11032: fix(security): block plugin install/load on critical source scan fi...
by coygeek · 2026-02-07
75.8%
#13176: fix: resolve llm-task module import for global installs
by striking · 2026-02-10
74.9%
#14292: fix(scripts): add js extension
by hannahhoward · 2026-02-11
74.3%
#15618: fix(plugins): reject async plugin registration instead of silently ...
by AI-Reviewer-QS · 2026-02-13
74.3%
#12475: fix(logging): use Symbol.for for externalTransports to survive jiti...
by Yida-Dev · 2026-02-09
73.3%
#11096: fix(plugins): require explicit trust for workspace and external plu...
by T1mn · 2026-02-07
73.1%
#8073: fix(plugins): add --ignore-scripts to npm install
by yubrew · 2026-02-03
73.0%