Skip to content

fix(md): run client scripts in builds without an .island. file - #681

Merged
cpojer merged 1 commit into
voidzero-dev:mainfrom
morinokami:fix/md-client-script-build
Nov 3, 2026
Merged

cpojer merged 1 commit into
voidzero-dev:mainfrom
morinokami:fix/md-client-script-build

Conversation

@morinokami

Copy link
Copy Markdown
Contributor

A client script in a Markdown page ran in dev but not after a production build, unless a file with .island. in its name existed under pages/. Without one, dist/client/.vite/manifest.json had no virtual:void-md-client entry, so the script never loaded.

The config hook in voidMarkdown() added that entry only when hasMdFilesWithScript(pagesDir) && hasIslandFiles(pagesDir) was true. hasIslandFiles only read file names under pages/, so:

  • A Markdown page that imports an island did not count. The island hydrated, but the rest of the script did not run.
  • A .island. file outside pages/ did not count.
  • Any matching name counted, even an empty pages/_x.island.txt.

Fix

The entry is now added when a page that the build keeps has client code. The check uses the same file list and extractScript() as scanMdPages(), so it agrees with what the entry loads.

This also replaces the /^<script\b/m check. It matched a script with only island imports, a <script> in a code block and a .dev page, none of which has client code in a build. With a .island. file present, those apps built a 0-byte md-client chunk that their pages with JavaScript loaded. They now get no entry.

The guide already describes this behavior, so the docs do not change.

Tests

  • packages/md/test/integration/client-script-build.test.ts (new): builds a React app with no .island. file under pages/ and opens it in Chromium. A script-only page and a page that also imports an island both run their scripts, the island responds to a click, and a page without client code ships no <script>. On main it fails because the manifest has no virtual:void-md-client.
  • packages/md/test/unit/plugin-source-dir.test.ts: covers which pages add the entry. Four cases fail on main, and four fail if only hasIslandFiles is removed.

Verification

  • vp test run: 765 files pass, 1 skipped (15,607 tests pass, 29 skipped).
  • vp run check: 33/33 pass.
  • By hand on React, Vue, Svelte and Solid, with vp build, vp preview (Cloudflare target) and Chromium: client scripts run, and an app without client code gets no md-client entry. Svelte and Solid used an absolute island import because of the first issue below, and the Solid island did not respond because of the second.

The Markdown client entry joined the client build only when a file with
`.island.` in its name existed under `pages/`. Without one, a page's
client script ran in dev but not after a production build, even when the
page imported an island.

Add the entry when a page the build keeps has client code, using the
same file list and script extraction as the Markdown page scan. A script
with only island imports, a `<script>` in a code block and a `.dev` page
no longer count. The old check also matched these, so an app with a
`.island.` file could build an empty entry that its pages with
JavaScript loaded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J8F8maan2SAVMkNT2gaiUH

@cpojer cpojer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for sending the PR!

@cpojer
cpojer merged commit 58998de into voidzero-dev:main Nov 3, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants