diff --git a/scripts/stage-bundled-plugin-runtime.mjs b/scripts/stage-bundled-plugin-runtime.mjs index d6585d3191a..07fd9e958f0 100644 --- a/scripts/stage-bundled-plugin-runtime.mjs +++ b/scripts/stage-bundled-plugin-runtime.mjs @@ -88,10 +88,29 @@ function stagePluginRuntimeOverlay(sourceDir, targetDir) { function linkPluginNodeModules(params) { const runtimeNodeModulesDir = path.join(params.runtimePluginDir, "node_modules"); removePathIfExists(runtimeNodeModulesDir); + if (params.distPluginDir) { + removePathIfExists(path.join(params.distPluginDir, "node_modules")); + } if (!fs.existsSync(params.sourcePluginNodeModulesDir)) { return; } fs.symlinkSync(params.sourcePluginNodeModulesDir, runtimeNodeModulesDir, symlinkType()); + + // Runtime wrappers re-export from dist/extensions//index.js, so Node + // resolves bare-specifier dependencies relative to the dist plugin directory. + // copy-bundled-plugin-metadata removes dist node_modules; restore the link here. + if (params.distPluginDir) { + removePathIfExists(path.join(params.distPluginDir, "node_modules")); + } + if (!fs.existsSync(params.sourcePluginNodeModulesDir)) { + return; + } + fs.symlinkSync(params.sourcePluginNodeModulesDir, runtimeNodeModulesDir, symlinkType()); + + if (params.distPluginDir) { + const distNodeModulesDir = path.join(params.distPluginDir, "node_modules"); + fs.symlinkSync(params.sourcePluginNodeModulesDir, distNodeModulesDir, symlinkType()); + } } export function stageBundledPluginRuntime(params = {}) { @@ -121,6 +140,7 @@ export function stageBundledPluginRuntime(params = {}) { stagePluginRuntimeOverlay(distPluginDir, runtimePluginDir); linkPluginNodeModules({ runtimePluginDir, + distPluginDir, sourcePluginNodeModulesDir, }); } diff --git a/src/plugins/stage-bundled-plugin-runtime.test.ts b/src/plugins/stage-bundled-plugin-runtime.test.ts index fe246e8fcfe..fef9a725799 100644 --- a/src/plugins/stage-bundled-plugin-runtime.test.ts +++ b/src/plugins/stage-bundled-plugin-runtime.test.ts @@ -49,6 +49,12 @@ describe("stageBundledPluginRuntime", () => { expect(fs.realpathSync(path.join(runtimePluginDir, "node_modules"))).toBe( fs.realpathSync(sourcePluginNodeModulesDir), ); + + // dist/ also gets a node_modules symlink so bare-specifier resolution works + // from the actual code location that the runtime wrapper re-exports into + const distNodeModules = path.join(distPluginDir, "node_modules"); + expect(fs.lstatSync(distNodeModules).isSymbolicLink()).toBe(true); + expect(fs.realpathSync(distNodeModules)).toBe(fs.realpathSync(sourcePluginNodeModulesDir)); }); it("writes wrappers that forward plugin entry imports into canonical dist files", async () => { diff --git a/ui/src/styles/config.css b/ui/src/styles/config.css index f3d76ab2e6e..b091b74d67c 100644 --- a/ui/src/styles/config.css +++ b/ui/src/styles/config.css @@ -715,6 +715,13 @@ line-height: 1.55; } +.config-raw-field .config-raw-toggle { + width: 32px; + height: 32px; + padding: 6px; + min-width: 32px; +} + /* Loading State */ .config-loading { display: flex; diff --git a/ui/src/ui/views/config.ts b/ui/src/ui/views/config.ts index 1ec032e352f..7c1121e6bb8 100644 --- a/ui/src/ui/views/config.ts +++ b/ui/src/ui/views/config.ts @@ -1060,7 +1060,7 @@ export function renderConfig(props: ConfigProps) { ` : nothing } -