FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

fix(tui): bridge node_modules @opentui imports so npm-spec TUI plugins load by lindeberg · Pull Request #33885 · anomalyco/opencode · GitHub

fix(tui): bridge node_modules @opentui imports so npm-spec TUI plugins load - #33885

Closed
lindeberg wants to merge 1 commit into
anomalyco:devfrom
lindeberg:fix-npm-spec-tui-plugin-loading
Closed

fix(tui): bridge node_modules @opentui imports so npm-spec TUI plugins load#33885
lindeberg wants to merge 1 commit into
anomalyco:devfrom
lindeberg:fix-npm-spec-tui-plugin-loading

Conversation

Copy link
Copy Markdown

What this fixes

Fixes #33884.

Since v1.17.10, TUI plugins referenced by their npm package spec in tui.json (e.g. {"plugin": ["@slkiser/opencode-quota"]}) silently stop loading — no sidebar panel, no slash commands, no visible error. Server plugins and file:// TUI plugins are unaffected.

In plain terms

opencode installs an npm TUI plugin into its own isolated folder under ~/.cache/opencode/packages/<spec>/node_modules/<name>/. Because that folder has its own node_modules, the plugin loads its own copy of @opentui/solid instead of the one the opencode app is already running. OpenTUI's Solid renderer keeps a module-level RendererContext singleton, so "the app's renderer" and "the plugin's renderer" end up being two different objects. The plugin then tries to register its panel/commands against a renderer the app isn't using, so nothing shows up — and the failure happens deep in module resolution, so no error reaches the user.

A file:// plugin doesn't have this problem because it lives outside any node_modules directory, so OpenTUI's loader already rewrites its @opentui/* imports onto the app's instance. The npm-spec path is the only one that broke, and it broke when OpenTUI 0.4.2 (#33610) introduced dual bun/node entry files for @opentui/solid, which made the isolated copy diverge from the host copy.

Technical detail

OpenTUI's runtime plugin bridges bare @opentui/* imports onto the host's registered runtime modules, but for paths inside node_modules that bridging is gated behind rewrite options that default to nodeModulesBareSpecifiers: false. Non-node_modules (i.e. file://) plugins are bridged unconditionally.

The fix enables nodeModulesBareSpecifiers when opencode installs the OpenTUI Solid runtime support, so bare @opentui/* imports inside the plugin cache tree are rewritten onto the host's registered instances — giving npm-spec plugins the same renderer instance the host uses, exactly like file:// plugins already get.

ensureRuntimePluginSupport({
  additional: keymapRuntimeModules,
  rewrite: { nodeModulesBareSpecifiers: true },
})

One line of behavior change in packages/opencode/src/plugin/tui/runtime.ts, plus an explanatory comment.

Verification

Reproduced on macOS, opencode 1.17.10 (Homebrew), Bun 1.3.14, using @slkiser/opencode-quota:

  • The host (Bun) resolves @opentui/solid to index.bun.js; the isolated plugin tree resolves a different @opentui/solid (index.js).
  • Loading the runtime plugin with the current defaults and importing the plugin's dist/tui.tsx from node_modules yields host RendererContext === plugin RendererContext → false (separate instances), matching the reported silent failure.
  • The file:// workaround works precisely because that path is bridged unconditionally.

See #33884 for the full investigation and evidence.

Notes for reviewers

  • This relies on the rewrite plumbing already present in @opentui/core's runtime plugin (nodeModulesBareSpecifiers), so no upstream OpenTUI change is required.
  • Worth a sanity check that enabling bare-specifier rewriting in node_modules doesn't pull additional packages through the rewrite loader in a way that affects startup; in testing it was scoped to the registered runtime specifiers (@opentui/*, solid-js, solid-js/store).

npm-spec TUI plugins are installed into an isolated cache tree under
node_modules, so they resolve their own copy of @opentui/solid and end up
with a separate RendererContext singleton. As of OpenTUI 0.4.2 (dual
bun/node entry files), that copy no longer matches the host's, so the
plugin's slots and commands silently fail to attach to the host renderer.

Enable nodeModulesBareSpecifiers rewriting so bare @opentui/* imports inside
node_modules are bridged onto the host's registered runtime instances, the
same behavior file:// plugins already get.
github-actions Bot added the needs:compliance This means the issue will auto-close after 2 hours. label Jun 25, 2026

Copy link
Copy Markdown
Contributor

This PR doesn't fully meet our contributing guidelines and PR template.

What needs to be fixed:

  • PR description is missing required template sections. Please use the PR template.

Please edit this PR description to address the above within 2 hours, or it will be automatically closed.

If you believe this was flagged incorrectly, please let a maintainer know.

Copy link
Copy Markdown
Author

Closing this — it's both ineffective and redundant.

Redundant: dev already reverted the OpenTUI 0.4.2 bump in #33842 (merged), so @opentui/solid is back to 0.3.4 (single-entry) where npm-spec TUI plugins load fine. The regression no longer exists on dev.

Ineffective: I verified the nodeModulesBareSpecifiers: true change in this PR does not actually fix the bug against the real 0.4.2 packaging. With the flag on, importing the plugin's dist/tui.tsx from node_modules still throws Export named 'jsxDEV' not found ... jsx-runtime.d.ts, and host RendererContext === plugin RendererContext is still false.

Root cause of why the flag is insufficient (for whoever re-attempts the 0.4.2 bump): the proximate failure is OpenTUI's Solid babel transform (@opentui/solid/scripts/solid-plugin.js), whose file filter hardcodes a node_modules exclusion:

const sourceFilter = input.resolvePath
  ? /^(?!.*[/\\]node_modules[/\\]).*\.[cm]?[jt]sx?(?:[?#].*)?$/
  : /^(?!.*[/\\]node_modules[/\\]).*\.[cm]?[jt]sx(?:[?#].*)?$/;

npm-spec plugins live under ~/.cache/opencode/.../node_modules/, so the Solid transform never runs on them, Bun falls back to native JSX, and @jsxImportSource @opentui/solid resolves @opentui/solid/jsx-runtime to the plugin's bundled stub. That exclusion is in a different Bun plugin and is not gated by nodeModulesBareSpecifiers. There's also a version-skew dimension (plugin bundles its own older @opentui/solid, distinct instance from the host). Details in #33884.

So: the revert is the correct fix for now. If 0.4.2 is re-attempted, the durable fix is to make npm-spec plugins resolve the host's @opentui/* (dedup/override at install) and/or relax the Solid transform's node_modules exclusion for the plugin cache tree — not this flag.

This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs:compliance This means the issue will auto-close after 2 hours.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TUI plugins referenced by npm package spec silently fail to load in 1.17.10 (OpenTUI 0.4.2 dual-entry regression)

1 participant


Back | FazBrowse Home | New Git URL