@@ -175,16 +175,18 @@ export function prerenderPlugin({ prerenderScript, renderTarget, additionalPrere
175175 if ( ssrBuild ) return ;
176176 // We're only going to alter the chunking behavior in the default cases, where the user and/or
177177 // other plugins haven't already configured this. It'd be impossible to avoid breakages otherwise.
178+ // Vite 8 renamed `rollupOptions` to `rolldownOptions` :( -> fall back to rollupOptions for Vite 5–7
179+ const _buildOpts = config . build . rolldownOptions ?? config . build . rollupOptions ;
178180 if (
179- Array . isArray ( config . build . rollupOptions . output ) ||
180- config . build . rollupOptions . output ?. manualChunks
181+ Array . isArray ( _buildOpts . output ) ||
182+ _buildOpts . output ?. manualChunks
181183 ) {
182184 viteConfig = config ;
183185 return ;
184186 }
185187
186- config . build . rollupOptions . output ??= { } ;
187- config . build . rollupOptions . output . manualChunks = ( id ) => {
188+ _buildOpts . output ??= { } ;
189+ _buildOpts . output . manualChunks = ( id ) => {
188190 if ( id . includes ( prerenderScript ) || id . includes ( preloadPolyfillId ) ) {
189191 return 'index' ;
190192 }
@@ -233,17 +235,13 @@ export function prerenderPlugin({ prerenderScript, renderTarget, additionalPrere
233235 } ;
234236 } else if ( id . includes ( preloadPolyfillId ) ) {
235237 const s = new MagicString ( code ) ;
236- // Replacement for ` 'link'` && ` "link"` as the output from their tooling has
237- // differed over the years. Should be better than switching to regex .
238+ // Matches both quote styles ( 'link' / "link") and optional semicolons, as the
239+ // compiled output has varied across Vite/Rollup/Rolldown versions over the years .
238240 // https://github.com/vitejs/vite/blob/20fdf210ee0ac0824b2db74876527cb7f378a9e8/packages/vite/src/node/plugins/modulePreloadPolyfill.ts#L62
239241 s . replace (
240- ` const relList = document.createElement(' link') .relList;` ,
242+ / c o n s t r e l L i s t = d o c u m e n t \ .c r e a t e E l e m e n t \( [ " ' ] l i n k [ " ' ] \) \ .r e l L i s t ; ? / ,
241243 `if (typeof window === "undefined") return;\n const relList = document.createElement('link').relList;` ,
242244 ) ;
243- s . replace (
244- `const relList = document.createElement("link").relList;` ,
245- `if (typeof window === "undefined") return;\n const relList = document.createElement("link").relList;` ,
246- ) ;
247245 return {
248246 code : s . toString ( ) ,
249247 map : s . generateMap ( { hires : true } ) ,
@@ -388,6 +386,15 @@ export function prerenderPlugin({ prerenderScript, renderTarget, additionalPrere
388386 /** @type {Partial<import('./types.d.ts').Head> } */
389387 let head = { lang : '' , title : '' , elements : new Set ( ) } ;
390388
389+ // Vite 8 native Rolldown path: modulepreload polyfill is injected by rolldown/experimental
390+ // and bypasses Vite's transform hook, so our window-check patch never runs. Stub document
391+ // only for the duration of the import so the polyfill's feature-detection exits early
392+ // (`relList.supports('modulepreload') === true`), without affecting user prerender code.
393+ // @ts -ignore
394+ const _documentStubbed = ! ( 'document' in globalThis ) ;
395+ // @ts -ignore
396+ if ( _documentStubbed ) globalThis . document = { createElement : ( ) => ( { relList : { supports : ( ) => true } } ) } ;
397+
391398 let prerender ;
392399 try {
393400 const m = await import (
@@ -397,6 +404,9 @@ export function prerenderPlugin({ prerenderScript, renderTarget, additionalPrere
397404 } catch ( e ) {
398405 const message = await handlePrerenderError ( e ) ;
399406 this . error ( message ) ;
407+ } finally {
408+ // @ts -ignore
409+ if ( _documentStubbed ) delete globalThis . document ;
400410 }
401411
402412 if ( typeof prerender !== 'function' ) {
0 commit comments