Skip to content

[beta] Prefetch placeholders not replaced when used with ClientRouter (dep optimization bypasses transform) #15520

@mwolson

Description

@mwolson

Astro Info

Astro                    v6.0.0-beta.11
Node                     v24.13.1
System                   Linux (x64)
Package Manager          bun
Output                   static
Adapter                  none
Integrations             @astrojs/mdx
                         @astrojs/sitemap

If this issue only occurs in one browser, which browser is a problem?

All browsers (Chrome, Firefox tested)

Describe the Bug

When prefetch is enabled and <ClientRouter /> is used, the dev server throws in the browser console:

Uncaught ReferenceError: __PREFETCH_PREFETCH_ALL__ is not defined
    at index.js:6:19

The source is node_modules/astro/dist/prefetch/index.js, which uses placeholder constants (__PREFETCH_PREFETCH_ALL__, __PREFETCH_DEFAULT_STRATEGY__, __EXPERIMENTAL_CLIENT_PRERENDER__) that the astroPrefetch Vite plugin replaces via its transform() hook.

The problem is that when <ClientRouter /> is used, Vite pulls astro/virtual-modules/prefetch.js into its dependency optimization (pre-bundling via esbuild) as a transitive dependency through the transitions import chain. Vite's dep optimizer uses esbuild, which does not invoke Vite plugin transform hooks. The pre-bundled output retains the raw placeholder identifiers.

This can be confirmed by inspecting the injected page script and the cached dep:

# The page script imports from the optimized dep path:
curl -s http://localhost:4321/@id/astro:scripts/page.js
# -> import { init } from "/node_modules/.vite/deps/astro_virtual-modules_prefetch__js.js?v=...";init()

# The optimized dep has unreplaced placeholders:
grep "PREFETCH_PREFETCH_ALL" node_modules/.vite/deps/astro_virtual-modules_prefetch__js.js
# -> var prefetchAll = __PREFETCH_PREFETCH_ALL__;

Meanwhile, requesting the module directly through Vite's transform pipeline shows the replacement works correctly:

curl -s http://localhost:4321/node_modules/astro/dist/prefetch/index.js | grep prefetchAll
# -> let prefetchAll = true                     ;

Root cause: The astro:transitions plugin avoids this same class of issue for its own placeholders because its transform targets .astro files which are never pre-bundled, and it uses config() to configure optimizeDeps. The astroPrefetch plugin does not add an optimizeDeps.exclude entry for its module.

Suggested fix -- add to vite-plugin-prefetch.js:

config() {
  return {
    optimizeDeps: {
      exclude: ["astro/virtual-modules/prefetch.js"],
    },
  };
},

What's the expected result?

The prefetch placeholders should be replaced before reaching the browser. No console errors when prefetch is enabled in the config.

Link to Minimal Reproducible Example

Verified reproduction steps:

  1. npm create astro@latest -- --template minimal
  2. npx @astrojs/upgrade beta (or bun add astro@6.0.0-beta.11)
  3. Edit astro.config.mjs:
    export default defineConfig({
      prefetch: {
        prefetchAll: true,
      },
    });
  4. Edit src/pages/index.astro to add <ClientRouter />:
    ---
    import { ClientRouter } from 'astro:transitions';
    ---
    <html lang="en">
      <head>
        <title>Astro</title>
        <ClientRouter />
      </head>
      <body><h1>Astro</h1></body>
    </html>
  5. npm run dev
  6. Open browser dev tools console -- ReferenceError: __PREFETCH_PREFETCH_ALL__ is not defined

StackBlitz: https://astro.new/minimal?on=stackblitz (apply steps 2-4 above)

User-side workaround:

// astro.config.mjs
vite: {
  optimizeDeps: {
    exclude: ["astro/virtual-modules/prefetch.js"],
  },
}

Participation

  • I am willing to submit a pull request for this issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    - P2: has workaroundAn edge case that only affects very specific usage, but has a trivial workaround (priority)pkg: astroRelated to the core `astro` package (scope)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions