fix: validate sandbox plugin exports and fix plugin packaging#363
fix: validate sandbox plugin exports and fix plugin packaging#363
Conversation
The webhook-notifier plugin exported raw TypeScript source from its
package.json exports (./sandbox pointed to src/sandbox-entry.ts).
When the Vite plugin resolved this at site build time, it embedded
unbuilt TypeScript into the sandbox module, causing "Unexpected token
'{'" errors at runtime.
Add a tsdown build step (matching sandboxed-test's pattern) and update
the exports map to point to dist/*.mjs.
Fixes #150
…le validator Add two validation checks to prevent plugins with misconfigured exports from silently breaking site builds: 1. generateSandboxedPluginsModule() now throws a clear error if a sandbox entrypoint resolves to a TypeScript/JSX source file instead of pre-built JavaScript. This catches the problem at site build time with an actionable message. 2. The `emdash bundle` command now validates that all package.json exports point to built files (.js/.mjs), not source (.ts/.tsx/.jsx). This catches the misconfiguration at plugin publish time, before consumers are affected. Fixes #150
The test file override for e18e/prefer-static-regex used parenthesis
syntax ("e18e(prefer-static-regex)") which is the diagnostic display
format, not the config format. Changed to slash syntax to match the
top-level rule declarations so the override actually takes effect.
Add tests for both validation checks: - generateSandboxedPluginsModule: verifies it embeds pre-built JS, rejects .ts/.tsx/.mts source files, and includes the plugin ID in error messages. - findSourceExports: verifies it flags .ts/.tsx/.mts/.cts/.jsx exports, accepts .mjs/.js exports, and handles conditional export maps. Also extracts findSourceExports() from the inline bundle.ts validation into bundle-utils.ts so it can be tested without the CLI harness.
🦋 Changeset detectedLatest commit: 27311b9 The changes in this PR will be included in the next version bump. This PR includes changesets to release 9 packages
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 |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
emdash-playground | 27311b9 | Apr 07 2026, 08:36 PM |
Same issue as webhook-notifier — both plugins exported raw TypeScript source from their package.json sandbox exports. Add tsdown build steps and update exports to point to dist/*.mjs.
@emdash-cms/admin
@emdash-cms/auth
@emdash-cms/blocks
@emdash-cms/cloudflare
emdash
create-emdash
@emdash-cms/gutenberg-to-portable-text
@emdash-cms/x402
@emdash-cms/plugin-ai-moderation
@emdash-cms/plugin-atproto
@emdash-cms/plugin-audit-log
@emdash-cms/plugin-color
@emdash-cms/plugin-embeds
@emdash-cms/plugin-forms
@emdash-cms/plugin-webhook-notifier
commit: |
There was a problem hiding this comment.
Pull request overview
Fixes a sandboxed-plugin runtime failure caused by plugins exporting unbuilt TypeScript by (1) correcting webhook-notifier’s packaging to export built dist/*.mjs, and (2) adding build-time + bundle-time validation to catch similar misconfigurations earlier.
Changes:
- Update
@emdash-cms/plugin-webhook-notifierto build withtsdownand export builtdistentrypoints (including./sandbox). - Add validation in
generateSandboxedPluginsModule()to error if a sandbox entry resolves to TS/JSX source. - Add
emdash bundlevalidation + unit tests to flagpackage.jsonexports pointing to TS/JSX source, and fix an oxlint override key.
Reviewed changes
Copilot reviewed 10 out of 11 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| pnpm-lock.yaml | Adds tsdown/typescript to the workspace lockfile for the plugin build. |
| packages/plugins/webhook-notifier/package.json | Switches exports from src/* to built dist/* and adds tsdown build/dev scripts. |
| packages/core/tests/unit/cli/bundle-utils.test.ts | Adds test coverage for new findSourceExports() validation. |
| packages/core/tests/unit/astro/virtual-modules-sandbox.test.ts | Adds tests ensuring sandbox module generation rejects TS/TSX/MTS entries and includes plugin id in errors. |
| packages/core/src/cli/commands/bundle.ts | Adds bundle-time validation to error on source-file exports. |
| packages/core/src/cli/commands/bundle-utils.ts | Introduces findSourceExports() helper used by bundle validation. |
| packages/core/src/astro/integration/virtual-modules.ts | Adds build-time guard to reject sandbox entrypoints that resolve to TS/JSX source. |
| .oxlintrc.json | Fixes override rule key syntax so the rule is actually disabled for that override. |
| .changeset/major-boxes-juggle.md | Adds changeset entries for emdash and @emdash-cms/plugin-webhook-notifier. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export function findSourceExports( | ||
| exports: Record<string, unknown>, | ||
| ): Array<{ exportPath: string; resolvedPath: string }> { | ||
| const issues: Array<{ exportPath: string; resolvedPath: string }> = []; | ||
| for (const [exportPath, exportValue] of Object.entries(exports)) { | ||
| const resolved = | ||
| typeof exportValue === "string" | ||
| ? exportValue | ||
| : exportValue && typeof exportValue === "object" && "import" in exportValue | ||
| ? (exportValue as { import: string }).import | ||
| : null; |
There was a problem hiding this comment.
findSourceExports() only inspects string exports and conditional exports with an import field. Valid package.json export objects can also use default/require (and can nest conditions), so a misconfigured plugin could still export ./src/*.ts and bypass this validation. Consider recursively walking export values and checking all string targets (excluding types) across common conditions (import/require/default) so the bundle-time check matches its intent.
…ive pnpm build
The build verification section was running `astro build` individually
and sequentially for every demo and template (~12 sites). Replace with
a single `pnpm run --recursive --filter {./demos/*} --filter
{./templates/*} build` which pnpm parallelizes automatically.
What does this PR do?
The
webhook-notifierplugin exported raw TypeScript source from itspackage.json("./sandbox": "./src/sandbox-entry.ts"). When the Vite plugin resolved this at site build time viarequire.resolve(), it embedded unbuilt TypeScript into the sandbox module, causingUnexpected token '{'errors at runtime.The root cause is a missing build step —
sandboxed-testalready had the correct pattern (tsdown build +dist/*.mjsexports), butwebhook-notifierwas pointing directly at source.This PR:
Fixes webhook-notifier — adds a tsdown build step and updates
package.jsonexports to point todist/*.mjs(matchingsandboxed-test's pattern).Adds build-time validation —
generateSandboxedPluginsModule()now throws a clear error if a sandbox entrypoint resolves to a TypeScript/JSX source file, catching misconfigured plugins at site build time.Adds bundle-time validation —
emdash bundlenow checks that allpackage.jsonexports point to built files, catching the misconfiguration at plugin publish time before consumers are affected.Fixes oxlintrc override syntax — the
e18e/prefer-static-regextest override used parenthesis syntax (e18e(prefer-static-regex)) instead of slash syntax, so the override wasn't taking effect.Closes #150
Type of change
Checklist
pnpm typecheckpassespnpm --silent lint:json | jq '.diagnostics | length'returns 0pnpm testpasses (or targeted tests for my change)pnpm formathas been runAI-generated code disclosure
Screenshots / test output