Skip to content

fix(core): fail fast with an actionable error for plugins without an entrypoint (#1416)#1560

Open
marcusbellamyshaw-cell wants to merge 2 commits into
emdash-cms:mainfrom
Emdash-Bug-Testing:fix/1416-plugins-missing-entrypoint
Open

fix(core): fail fast with an actionable error for plugins without an entrypoint (#1416)#1560
marcusbellamyshaw-cell wants to merge 2 commits into
emdash-cms:mainfrom
Emdash-Bug-Testing:fix/1416-plugins-missing-entrypoint

Conversation

@marcusbellamyshaw-cell

Copy link
Copy Markdown
Contributor

What does this PR do?

generatePluginsModule assumed every plugin descriptor had a file entrypoint and emitted import pluginDefN from "${descriptor.entrypoint}";. For an in-process plugin — an inline definePlugin({...}) result passed directly to plugins: []entrypoint is undefined, so the generated virtual module contained import pluginDef0 from "undefined"; and the build failed deep in Rollup with the cryptic Rollup failed to resolve import "undefined" from "virtual:emdash/plugins".

The generator now throws a clear, actionable error that names the offending plugin and explains that plugins: [] entries must resolve to a file/package entrypoint (bundled at build time), so an inline definePlugin({...}) result is not supported — move the plugin into its own module and reference it via a factory that returns a descriptor with an entrypoint.

Closes #1416

Type of change

  • Bug fix

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes (targeted: tests/unit/plugins — 600 tests)
  • pnpm format has been run
  • I have added/updated tests for my changes
  • I have added a changeset

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: Claude Opus 4.8 ultracode

…entrypoint (emdash-cms#1416)

generatePluginsModule assumed every plugin descriptor had a file entrypoint and
emitted `import pluginDefN from "${descriptor.entrypoint}";`. For an in-process
plugin -- an inline definePlugin({...}) result passed directly to plugins: [] --
entrypoint is undefined, so the generated virtual module contained
`import pluginDef0 from "undefined";` and the build failed deep in Rollup with
the cryptic `Rollup failed to resolve import "undefined" from
"virtual:emdash/plugins"`.

The generator now throws a clear error that names the offending plugin and
explains that plugins: [] entries must resolve to a file/package entrypoint
(bundled at build time), so an inline definePlugin({...}) result is not
supported -- move the plugin into its own module and reference it via a factory
that returns a descriptor with an entrypoint.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jun 20, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d19e6a7

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 16 packages
Name Type
emdash Patch
@emdash-cms/cloudflare Patch
@emdash-cms/sandbox-workerd Patch
@emdash-cms/fixture-perf-site Patch
@emdash-cms/perf-demo-site Patch
@emdash-cms/cache-demo-site Patch
@emdash-cms/do-demo-site Patch
@emdash-cms/do-solo-demo-site Patch
@emdash-cms/admin Patch
@emdash-cms/auth Patch
@emdash-cms/blocks Patch
@emdash-cms/gutenberg-to-portable-text Patch
@emdash-cms/x402 Patch
create-emdash Patch
@emdash-cms/auth-atproto Patch
@emdash-cms/plugin-embeds Patch

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

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approach

This is the right change at the right boundary. EmDashConfig.plugins expects PluginDescriptor[] — descriptors whose entrypoint is bundled at build time and imported at runtime — which is a distinct layer from RuntimeDependencies.plugins: ResolvedPlugin[] (the in-process, hooks/routes-bearing shape that definePlugin({...}) returns, consumed by EmDashRuntime.create()). The #1416 reporter conflated the two by passing a definePlugin({...}) result (a ResolvedPlugin, no entrypoint) straight into plugins: []; the generator then emitted import pluginDef0 from "undefined"; and died deep in Rollup. Catching this at the descriptor→codegen boundary is correct, and it does not break the legitimate in-process runtime plugin path (that goes through RuntimeDependencies.plugins, never generatePluginsModule). Turning the cryptic Rollup resolution error into a named, actionable build error is a clear improvement, and the change is purely additive: valid configs are unaffected, invalid ones still fail — just clearly.

What I checked

  • Traced generatePluginsModule → its only caller (the virtual:emdash/plugins load hook in vite-config.ts) → its only runtime importer (astro/middleware.ts). The middleware always imports the virtual module, so the guard fires on every build; the layering is sound and the error surfaces during the build rather than silently slipping through.
  • Confirmed PluginDescriptor.entrypoint is typed required, so the guard (if (!descriptor.entrypoint)) correctly catches undefined/null/"", and there's no legitimate falsy-entrypoint case to regress.
  • Confirmed the runtime in-process path (RuntimeDependencies.plugins: ResolvedPlugin[] in emdash-runtime.ts) is separate and untouched, so definePlugin results still work where they're actually supported.
  • The test reproduces the reporter's bypass-the-type scenario, asserts the message names the plugin and mentions entrypoint, and guards against the cryptic "undefined" regression. Good TDD coverage for the fix.
  • Changeset targets the right package (emdash) at the right bump (patch).

Findings

Two issues, neither a blocker:

  1. Changeset is framed as a commit message / PR description (needs_fixing, AGENTS.md convention). It leads with a fix(core): commit prefix and the body is a diff walkthrough naming the codegen function and quoting the exact emitted imports and the literal Rollup error string — the internal mechanics AGENTS.md says to leave out. Suggested a user-facing rewrite leading with Fixes.
  2. Sibling generateSandboxedPluginsModule lacks the same guard (suggestion). The identical misuse on sandboxed: [] still fails cryptically via require.resolve(undefined); the two generators are now inconsistent for the same bad input. Suggested a local guard or, preferably, hoisting the check into the shared config-time validation in index.ts that already iterates both arrays.

Implementation is clean and the fix achieves its stated goal. Verdict is comment — the changeset wording and the sandboxed-path consistency gap are worth addressing but don't block merge.


Findings

  • [needs fixing] .changeset/fix-1416-plugins-missing-entrypoint.md:5-9

    This changeset reads like a commit message / PR description, not release notes. The title line uses a fix(core): commit-message prefix, and the body is a diff walkthrough full of internal mechanics — the codegen function name (generatePluginsModule), the exact emitted import statements (import pluginDefN from "${descriptor.entrypoint}";, import pluginDef0 from "undefined";), and the literal Rollup error string (Rollup failed to resolve import "undefined" from "virtual:emdash/plugins").

    AGENTS.md (→ CONTRIBUTING.md § Changesets) is explicit: a changeset is release notes a user reads while upgrading — not a commit message, PR description, or summary of the diff; lead with a present-tense verb (Fixes, Adds, …), describe the observable effect, and leave out internal mechanics. The other recent changesets in this repo (session-get-hang.md, layout-prefetch.md, forward-plugin-blocks.md) all lead with a present-tense verb and center the user-facing effect; none use a type(scope): prefix.

    Fixes a cryptic build failure when a plugin in `plugins: []` has no `entrypoint` — for example, an inline `definePlugin({...})` result passed directly instead of a factory that returns a descriptor. The build now fails fast with an actionable error naming the offending plugin and explaining that `plugins: []` entries must resolve to a file or package entrypoint.
    
  • [suggestion] packages/core/src/astro/integration/virtual-modules.ts:582

    The new entrypoint guard lives only in generatePluginsModule (the plugins: [] path). The sibling generateSandboxedPluginsModule (the sandboxed: [] path) consumes descriptor.entrypoint here without the same guard, so the identical misuse — a descriptor with no entrypoint — still fails cryptically via require.resolve(undefined) (TypeError: The "id" argument must be of type string) instead of the actionable, plugin-named message this PR introduces. The two generators are now inconsistent for the same class of bad input.

    This is out of scope for issue #1416 (which is about plugins: []), so it's not blocking, but for consistency consider applying the same fail-fast guard here, or — better — hoisting the entrypoint check into the shared config-time validation block in index.ts that already iterates both pluginDescriptors and sandboxedDescriptors, so both arrays fail fast in one place before Vite's load hook runs.

    		if (!descriptor.entrypoint) {
    			throw new Error(
    				`[emdash] Sandboxed plugin "${descriptor.id}" has no \`entrypoint\`. \`sandboxed: []\` requires plugins that resolve to a file/package entrypoint so they can be bundled at build time.`,
    			);
    		}
    		const bundleSpecifier = descriptor.entrypoint;
    

@github-actions github-actions Bot added review/awaiting-author Reviewed; waiting on the author to respond and removed review/needs-review No maintainer or bot review yet labels Jun 20, 2026
…elease note

Per CONTRIBUTING.md, lead with the observable effect and drop the
commit-message title line and internal codegen mechanics.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions github-actions Bot added review/needs-rereview Author pushed changes since the last review and removed review/awaiting-author Reviewed; waiting on the author to respond labels Jun 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/core cla: signed review/needs-rereview Author pushed changes since the last review size/M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

virtual:emdash/plugins generates 'import undefined' for in-process plugins under env-replacement scenarios

1 participant