@@ -13,9 +13,35 @@ import { getAstroMetadata } from '../vite-plugin-astro/index.js';
1313import type { PluginMetadata } from '../vite-plugin-astro/types.js' ;
1414import { ASTRO_VITE_ENVIRONMENT_NAMES } from '../core/constants.js' ;
1515
16+ /**
17+ * A dev-only virtual module that exposes accumulated component metadata (containsHead, propagation)
18+ * as a serialized array that can be statically imported.
19+ *
20+ * This exists to serve pipelines that cannot do live module graph traversal at request time —
21+ * specifically `NonRunnablePipeline`, used by adapters like Cloudflare that run requests through
22+ * their own server runtime rather than Vite's runner. Those pipelines cannot call
23+ * `getComponentMetadata()` (which requires a `ModuleLoader`), so they import this virtual module
24+ * instead to get equivalent metadata.
25+ *
26+ * The `RunnablePipeline` does NOT use this module; it calls `getComponentMetadata()` directly,
27+ * which traverses the live Vite module graph and produces more accurate per-request data.
28+ *
29+ * The virtual module is invalidated whenever metadata propagation runs (on transform, resolveId)
30+ * and on file add/unlink, ensuring it stays fresh during HMR.
31+ */
32+ const VIRTUAL_COMPONENT_METADATA = 'virtual:astro:component-metadata' ;
33+ const RESOLVED_VIRTUAL_COMPONENT_METADATA = `\0${ VIRTUAL_COMPONENT_METADATA } ` ;
34+
1635export default function configHeadVitePlugin ( ) : vite . Plugin {
1736 let environment : DevEnvironment ;
1837
38+ function invalidateComponentMetadataModule ( ) {
39+ const virtualMod = environment . moduleGraph . getModuleById ( RESOLVED_VIRTUAL_COMPONENT_METADATA ) ;
40+ if ( virtualMod ) {
41+ environment . moduleGraph . invalidateModule ( virtualMod ) ;
42+ }
43+ }
44+
1945 function buildImporterGraphFromEnvironment ( seed : string ) {
2046 // Start from one changed/imported module and walk upward to collect ancestors.
2147 const queue : string [ ] = [ seed ] ;
@@ -65,16 +91,51 @@ export default function configHeadVitePlugin(): vite.Plugin {
6591 }
6692 }
6793 }
94+
95+ invalidateComponentMetadataModule ( ) ;
6896 }
6997
7098 return {
7199 name : 'astro:head-metadata' ,
72100 enforce : 'pre' ,
73101 apply : 'serve' ,
74- configureServer ( server ) {
75- environment = server . environments [ ASTRO_VITE_ENVIRONMENT_NAMES . ssr ] ;
102+ configureServer ( devServer ) {
103+ environment = devServer . environments [ ASTRO_VITE_ENVIRONMENT_NAMES . ssr ] ;
104+ devServer . watcher . on ( 'add' , invalidateComponentMetadataModule ) ;
105+ devServer . watcher . on ( 'unlink' , invalidateComponentMetadataModule ) ;
106+ devServer . watcher . on ( 'change' , invalidateComponentMetadataModule ) ;
107+ } ,
108+ load ( id ) {
109+ if ( id !== RESOLVED_VIRTUAL_COMPONENT_METADATA ) {
110+ return ;
111+ }
112+
113+ const componentMetadataEntries : [ string , SSRComponentMetadata ] [ ] = [ ] ;
114+ for ( const [ moduleId , mod ] of environment . moduleGraph . idToModuleMap ) {
115+ const info = this . getModuleInfo ( moduleId ) ?? ( mod . id ? this . getModuleInfo ( mod . id ) : null ) ;
116+ if ( ! info ) continue ;
117+
118+ const astro = getAstroMetadata ( info ) ;
119+ if ( ! astro ) continue ;
120+
121+ componentMetadataEntries . push ( [
122+ moduleId ,
123+ {
124+ containsHead : astro . containsHead ,
125+ propagation : astro . propagation ,
126+ } ,
127+ ] ) ;
128+ }
129+
130+ return {
131+ code : `export const componentMetadataEntries = ${ JSON . stringify ( componentMetadataEntries ) } ;` ,
132+ } ;
76133 } ,
77134 resolveId ( source , importer ) {
135+ if ( source === VIRTUAL_COMPONENT_METADATA ) {
136+ return RESOLVED_VIRTUAL_COMPONENT_METADATA ;
137+ }
138+
78139 if ( importer ) {
79140 // Do propagation any time a new module is imported. This is because
80141 // A module with propagation might be loaded before one of its parent pages
@@ -108,6 +169,8 @@ export default function configHeadVitePlugin(): vite.Plugin {
108169 // `// astro-head-inject` and `//! astro-head-inject` opt a module into bubbling.
109170 propagateMetadata . call ( this , id , 'propagation' , 'in-tree' ) ;
110171 }
172+
173+ invalidateComponentMetadataModule ( ) ;
111174 } ,
112175 } ;
113176}
0 commit comments