@@ -242,12 +242,57 @@ export class RenderContext {
242242 return result ;
243243 }
244244
245+ #astroPagePartial?: Omit < AstroGlobal , 'props' | 'self' | 'slots' > ;
246+ /**
247+ * The Astro global is sourced in 3 different phases:
248+ * - **Static**: `.generator` and `.glob` is printed by the compiler, instantiated once per process per astro file
249+ * - **Page-level**: `.request`, `.cookies`, `.locals` etc. These remain the same for the duration of the request.
250+ * - **Component-level**: `.props`, `.slots`, and `.self` are unique to each _use_ of each component.
251+ *
252+ * The page level partial is used as the prototype of the user-visible `Astro` global object, which is instantiated once per use of a component.
253+ */
245254 createAstro (
246255 result : SSRResult ,
247- astroGlobalPartial : AstroGlobalPartial ,
256+ astroStaticPartial : AstroGlobalPartial ,
248257 props : Record < string , any > ,
249258 slotValues : Record < string , any > | null
250259 ) : AstroGlobal {
260+ // Create page partial with static partial so they can be cached together.
261+ const astroPagePartial = ( this . #astroPagePartial ??= this . createAstroPagePartial (
262+ result ,
263+ astroStaticPartial
264+ ) ) ;
265+ // Create component-level partials. `Astro.self` is added by the compiler.
266+ const astroComponentPartial = { props, self : null } ;
267+
268+ // Create final object. `Astro.slots` will be lazily created.
269+ const Astro : Omit < AstroGlobal , 'self' | 'slots' > = Object . assign (
270+ Object . create ( astroPagePartial ) ,
271+ astroComponentPartial
272+ ) ;
273+
274+ // Handle `Astro.slots`
275+ let _slots : AstroGlobal [ 'slots' ] ;
276+ Object . defineProperty ( Astro , 'slots' , {
277+ get : ( ) => {
278+ if ( ! _slots ) {
279+ _slots = new Slots (
280+ result ,
281+ slotValues ,
282+ this . pipeline . logger
283+ ) as unknown as AstroGlobal [ 'slots' ] ;
284+ }
285+ return _slots ;
286+ } ,
287+ } ) ;
288+
289+ return Astro as AstroGlobal ;
290+ }
291+
292+ createAstroPagePartial (
293+ result : SSRResult ,
294+ astroStaticPartial : AstroGlobalPartial
295+ ) : Omit < AstroGlobal , 'props' | 'self' | 'slots' > {
251296 const renderContext = this ;
252297 const { cookies, locals, params, pipeline, request, url } = this ;
253298 const { response } = result ;
@@ -260,12 +305,10 @@ export class RenderContext {
260305 }
261306 return new Response ( null , { status, headers : { Location : path } } ) ;
262307 } ;
263- const slots = new Slots ( result , slotValues , pipeline . logger ) as unknown as AstroGlobal [ 'slots' ] ;
264308
265- // `Astro.self` is added by the compiler
266- const astroGlobalCombined : Omit < AstroGlobal , 'self' > = {
267- generator : astroGlobalPartial . generator ,
268- glob : astroGlobalPartial . glob ,
309+ return {
310+ generator : astroStaticPartial . generator ,
311+ glob : astroStaticPartial . glob ,
269312 cookies,
270313 get clientAddress ( ) {
271314 return renderContext . clientAddress ( ) ;
@@ -280,17 +323,13 @@ export class RenderContext {
280323 get preferredLocaleList ( ) {
281324 return renderContext . computePreferredLocaleList ( ) ;
282325 } ,
283- props,
284326 locals,
285327 redirect,
286328 request,
287329 response,
288- slots,
289330 site : pipeline . site ,
290331 url,
291332 } ;
292-
293- return astroGlobalCombined as AstroGlobal ;
294333 }
295334
296335 clientAddress ( ) {
0 commit comments