fix: bundle sandbox entries with esbuild & normalize CodeBlock languages#179
Conversation
🦋 Changeset detectedLatest commit: d50f98e The changes in this PR will be included in the next version bump. Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
I have read the CLA Document and I hereby sign the CLA You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot. |
The sandbox runner embeds plugin entrypoints at build time via
generateSandboxedPluginsModule(). Previously it used readFileSync()
to read the raw source and embed it as-is. This breaks when:
1. The entrypoint is TypeScript (.ts/.tsx) — Worker Loader executes
modules as JavaScript, so TS syntax like 'as { ... }' causes
'Unexpected token {' at runtime.
2. The entrypoint has bare imports (e.g. 'import { definePlugin }
from "emdash"') — Worker Loader only provides 'plugin.js' and
'sandbox-plugin.js' modules, so unresolved specifiers crash
during V8 module linking.
Replace readFileSync with an async esbuild.build() call that:
- Bundles all imports into a single self-contained ES module
- Transpiles TypeScript/TSX/JSX via the appropriate loader
- Shims 'emdash' with a virtual module that provides definePlugin
as an identity function (matching standard-format behaviour in
define-plugin.ts)
- Wraps errors with the plugin ID and file path for debuggability
- Handles .ts, .tsx, .mts, .cts, and .jsx extensions
generateSandboxedPluginsModule is now async (returns Promise<string>),
which is compatible with Vite's load() hook.
Kumo's CodeBlock component only supports 5 language values: ts, tsx, jsonc, bash, css. Passing any other value (e.g. 'json', 'javascript', 'shell') causes a TypeError because the component does an unguarded map lookup: KUMO_CODE_VARIANTS.lang[lang].classes Add a normalizeLang() function to CodeBlockComponent that: - Maps common aliases to their Kumo equivalents (json -> jsonc, javascript/js -> ts, sh/shell -> bash) - Falls back to 'bash' for unknown languages - Uses a local SupportedLang type to avoid coupling to Kumo internals Also: - Export a new CodeLanguage type from types.ts that includes both canonical Kumo values and accepted aliases - Update the codeBlock() builder to use CodeLanguage - Expand CODE_LANGUAGES in validation.ts to accept aliases - Re-export CodeBlock and CodeLanguage from server.ts
The Block Kit CodeBlock type and Kumo's CodeBlock component use 'jsonc' for JSON content. Both webhook-notifier and sandboxed-test were using 'json' which is not in the canonical set and would fail validation if validateBlocks() were enforced. While the blocks renderer now normalizes 'json' -> 'jsonc' as a safety net, first-party plugins should use the canonical value.
f5d9d79 to
d50f98e
Compare
|
Hi Yomna. Thanks for the PR. The bundling fix shouldn't be needed: the CLI already has a bundler for plugins and it's a bug in the webhook notifier plugin that's exporting the TypeScript source instead of the compiled JS. If you could scope this down to just the codeblock fix then I can merge it. Meanwhile I'll fix the webhook notifier plugin bug. |
Overlapping PRsThis PR modifies files that are also changed by other open PRs: This may cause merge conflicts or duplicated work. A maintainer will coordinate. |
Problem
Two bugs prevent sandboxed plugins from working correctly:
1. Sandbox entries fail with
Unexpected token '{'generateSandboxedPluginsModule()reads plugin entrypoints withreadFileSync()and embeds the raw source as a string in a Worker Loader isolate. This breaks when:routeCtx.input as { type: string }cause V8 to throwUnexpected token '{'.import { definePlugin } from "emdash"survive as-is. Worker Loader only providesplugin.jsandsandbox-plugin.jsmodules, so V8 fails during module linking.Repro: Configure any plugin in
sandboxed: [...], then visit its admin settings page. The API route returns:{"error":{"code":"ROUTE_ERROR","message":"Unexpected token '{'"}}2. CodeBlock crashes on unsupported
languagevaluesKumo's
CodeBlockcomponent has a hardcoded variant map with 5 languages (ts,tsx,jsonc,bash,css). Passing any other value (e.g."json") causes:The webhook-notifier plugin returns
language: "json"in its Block Kit code blocks, which crashes the admin UI after Bug 1 is fixed.Solution
Commit 1: Bundle sandbox entries with esbuild (
packages/core)Replace
readFileSync()with an asyncesbuild.build()call that:"emdash"with a virtual module providingdefinePluginas an identity function (matching standard-format behaviour indefine-plugin.ts— this follows the same pattern as the CLI bundler inpackages/core/src/cli/commands/bundle.ts:360-379)generateSandboxedPluginsModule()is nowasync(returnsPromise<string>), which is compatible with Vite'sload()hook.Commit 2: Normalize CodeBlock language values (
packages/blocks)normalizeLang()toCodeBlockComponentthat maps aliases (json→jsonc,javascript/js→ts,sh/shell→bash) and falls back to"bash"for unknown valuesCodeLanguagetype that includes both canonical and alias valuesCODE_LANGUAGESin validation to accept aliasescodeBlock()builder to useCodeLanguageCommit 3: Fix plugin language values (
packages/plugins)Change
language: "json"→"jsonc"inwebhook-notifierandsandboxed-testsandbox entries. First-party plugins should use canonical values; the renderer normalization is a safety net for third-party plugins.Testing
Verified with a Cloudflare Workers deployment:
astro buildsucceeds with plugins insandboxed: [...]var definePlugin = (d) => d;) and fully resolved, transpiled JavaScript/_emdash/admin/plugins/webhook-notifier/settingsrenders correctly — form fields, JSON code preview, and action buttons all workRelated
A separate PR to
cloudflare/kumoadds a defensive?.classes ??fallback incodeVariants()so Kumo itself doesn't crash on unknownlangvalues. That fix is defense-in-depth — this PR's blocks-layer normalization is sufficient on its own.