@@ -38,13 +38,11 @@ interface MergeSnapshot<Snapshot> {
3838 ) : void ;
3939}
4040
41- export interface ProfiledComponentOnlyFields < Snapshot > {
41+ export interface RenderStream < Snapshot extends ValidSnapshot > {
4242 // Allows for partial updating of the snapshot by shallow merging the results
4343 mergeSnapshot : MergeSnapshot < Snapshot > ;
4444 // Performs a full replacement of the snapshot
4545 replaceSnapshot : ReplaceSnapshot < Snapshot > ;
46- }
47- export interface ProfiledComponentFields < Snapshot > {
4846 /**
4947 * An array of all renders that have happened so far.
5048 * Errors thrown during component render will be captured here, too.
@@ -81,10 +79,6 @@ export interface ProfiledComponentFields<Snapshot> {
8179 waitForNextRender ( options ?: NextRenderOptions ) : Promise < Render < Snapshot > > ;
8280}
8381
84- export interface RenderStream < Snapshot extends ValidSnapshot >
85- extends ProfiledComponentFields < Snapshot > ,
86- ProfiledComponentOnlyFields < Snapshot > { }
87-
8882export interface RenderStreamWithRenderFn < Snapshot extends ValidSnapshot >
8983 extends RenderStream < Snapshot > {
9084 render : typeof baseRender ;
@@ -172,7 +166,7 @@ export function createRenderStream<Snapshot extends ValidSnapshot = void>({
172166 baseDuration,
173167 startTime,
174168 commitTime,
175- count : Profiler . renders . length + 1 ,
169+ count : stream . renders . length + 1 ,
176170 } ;
177171 try {
178172 /*
@@ -200,12 +194,12 @@ export function createRenderStream<Snapshot extends ValidSnapshot = void>({
200194 profilerContext . renderedComponents
201195 ) ;
202196 profilerContext . renderedComponents = [ ] ;
203- Profiler . renders . push ( render ) ;
197+ stream . renders . push ( render ) ;
204198 resolveNextRender ?.( render ) ;
205199 } catch ( error ) {
206- Profiler . renders . push ( {
200+ stream . renders . push ( {
207201 phase : "snapshotError" ,
208- count : Profiler . renders . length ,
202+ count : stream . renders . length ,
209203 error,
210204 } ) ;
211205 rejectNextRender ?.( error ) ;
@@ -245,110 +239,106 @@ export function createRenderStream<Snapshot extends ValidSnapshot = void>({
245239 } ) ;
246240 } ) as typeof baseRender ;
247241
248- let Profiler : RenderStreamWithRenderFn < Snapshot > = { } as any ;
249- Profiler = Object . assign (
250- Profiler as { } ,
251- {
252- replaceSnapshot,
253- mergeSnapshot,
254- } satisfies ProfiledComponentOnlyFields < Snapshot > ,
255- {
256- renders : new Array <
257- | Render < Snapshot >
258- | { phase : "snapshotError" ; count : number ; error : unknown }
259- > ( ) ,
260- totalRenderCount ( ) {
261- return Profiler . renders . length ;
262- } ,
263- async peekRender ( options : NextRenderOptions = { } ) {
264- if ( iteratorPosition < Profiler . renders . length ) {
265- const render = Profiler . renders [ iteratorPosition ] ;
266-
267- if ( render . phase === "snapshotError" ) {
268- throw render . error ;
269- }
242+ // creating the object first and then assigning in all the properties
243+ // allows keeping the object instance for reference while the members are
244+ // created, which is important for the `markAssertable` function
245+ let stream : RenderStreamWithRenderFn < Snapshot > = { } as any ;
246+ Object . assign < typeof stream , typeof stream > ( stream , {
247+ replaceSnapshot,
248+ mergeSnapshot,
249+ renders : new Array <
250+ | Render < Snapshot >
251+ | { phase : "snapshotError" ; count : number ; error : unknown }
252+ > ( ) ,
253+ totalRenderCount ( ) {
254+ return stream . renders . length ;
255+ } ,
256+ async peekRender ( options : NextRenderOptions = { } ) {
257+ if ( iteratorPosition < stream . renders . length ) {
258+ const render = stream . renders [ iteratorPosition ] ;
270259
271- return render ;
260+ if ( render . phase === "snapshotError" ) {
261+ throw render . error ;
272262 }
273- return Profiler . waitForNextRender ( {
274- [ _stackTrace ] : captureStackTrace ( Profiler . peekRender ) ,
263+
264+ return render ;
265+ }
266+ return stream . waitForNextRender ( {
267+ [ _stackTrace ] : captureStackTrace ( stream . peekRender ) ,
268+ ...options ,
269+ } ) ;
270+ } ,
271+ takeRender : markAssertable ( async function takeRender (
272+ options : NextRenderOptions = { }
273+ ) {
274+ // In many cases we do not control the resolution of the suspended
275+ // promise which results in noisy tests when the profiler due to
276+ // repeated act warnings.
277+ using _disabledActWarnings = disableActWarnings ( ) ;
278+
279+ let error : unknown = undefined ;
280+
281+ try {
282+ return await stream . peekRender ( {
283+ [ _stackTrace ] : captureStackTrace ( stream . takeRender ) ,
275284 ...options ,
276285 } ) ;
277- } ,
278- takeRender : markAssertable ( async function takeRender (
279- options : NextRenderOptions = { }
280- ) {
281- // In many cases we do not control the resolution of the suspended
282- // promise which results in noisy tests when the profiler due to
283- // repeated act warnings.
284- using _disabledActWarnings = disableActWarnings ( ) ;
285-
286- let error : unknown = undefined ;
287-
288- try {
289- return await Profiler . peekRender ( {
290- [ _stackTrace ] : captureStackTrace ( Profiler . takeRender ) ,
291- ...options ,
292- } ) ;
293- } catch ( e ) {
294- error = e ;
295- throw e ;
296- } finally {
297- if ( ! ( error && error instanceof WaitForRenderTimeoutError ) ) {
298- iteratorPosition ++ ;
299- }
300- }
301- } , Profiler ) ,
302- getCurrentRender ( ) {
303- // The "current" render should point at the same render that the most
304- // recent `takeRender` call returned, so we need to get the "previous"
305- // iterator position, otherwise `takeRender` advances the iterator
306- // to the next render. This means we need to call `takeRender` at least
307- // once before we can get a current render.
308- const currentPosition = iteratorPosition - 1 ;
309-
310- if ( currentPosition < 0 ) {
311- throw new Error (
312- "No current render available. You need to call `takeRender` before you can get the current render."
313- ) ;
286+ } catch ( e ) {
287+ error = e ;
288+ throw e ;
289+ } finally {
290+ if ( ! ( error && error instanceof WaitForRenderTimeoutError ) ) {
291+ iteratorPosition ++ ;
314292 }
293+ }
294+ } , stream ) ,
295+ getCurrentRender ( ) {
296+ // The "current" render should point at the same render that the most
297+ // recent `takeRender` call returned, so we need to get the "previous"
298+ // iterator position, otherwise `takeRender` advances the iterator
299+ // to the next render. This means we need to call `takeRender` at least
300+ // once before we can get a current render.
301+ const currentPosition = iteratorPosition - 1 ;
302+
303+ if ( currentPosition < 0 ) {
304+ throw new Error (
305+ "No current render available. You need to call `takeRender` before you can get the current render."
306+ ) ;
307+ }
315308
316- const render = Profiler . renders [ currentPosition ] ;
309+ const render = stream . renders [ currentPosition ] ;
317310
318- if ( render . phase === "snapshotError" ) {
319- throw render . error ;
320- }
321- return render ;
322- } ,
323- waitForNextRender ( {
324- timeout = 1000 ,
325- // capture the stack trace here so its stack trace is as close to the calling code as possible
326- [ _stackTrace ] : stackTrace = captureStackTrace (
327- Profiler . waitForNextRender
328- ) ,
329- } : NextRenderOptions = { } ) {
330- if ( ! nextRender ) {
331- nextRender = Promise . race < Render < Snapshot > > ( [
332- new Promise < Render < Snapshot > > ( ( resolve , reject ) => {
333- resolveNextRender = resolve ;
334- rejectNextRender = reject ;
335- } ) ,
336- new Promise < Render < Snapshot > > ( ( _ , reject ) =>
337- setTimeout ( ( ) => {
338- reject (
339- applyStackTrace ( new WaitForRenderTimeoutError ( ) , stackTrace )
340- ) ;
341- resetNextRender ( ) ;
342- } , timeout )
343- ) ,
344- ] ) ;
345- }
346- return nextRender ;
347- } ,
348- } satisfies ProfiledComponentFields < Snapshot > ,
349- { render }
350- ) ;
351- return Profiler ;
311+ if ( render . phase === "snapshotError" ) {
312+ throw render . error ;
313+ }
314+ return render ;
315+ } ,
316+ waitForNextRender ( {
317+ timeout = 1000 ,
318+ // capture the stack trace here so its stack trace is as close to the calling code as possible
319+ [ _stackTrace ] : stackTrace = captureStackTrace ( stream . waitForNextRender ) ,
320+ } : NextRenderOptions = { } ) {
321+ if ( ! nextRender ) {
322+ nextRender = Promise . race < Render < Snapshot > > ( [
323+ new Promise < Render < Snapshot > > ( ( resolve , reject ) => {
324+ resolveNextRender = resolve ;
325+ rejectNextRender = reject ;
326+ } ) ,
327+ new Promise < Render < Snapshot > > ( ( _ , reject ) =>
328+ setTimeout ( ( ) => {
329+ reject (
330+ applyStackTrace ( new WaitForRenderTimeoutError ( ) , stackTrace )
331+ ) ;
332+ resetNextRender ( ) ;
333+ } , timeout )
334+ ) ,
335+ ] ) ;
336+ }
337+ return nextRender ;
338+ } ,
339+ render,
340+ } ) ;
341+ return stream ;
352342}
353343
354344export class WaitForRenderTimeoutError extends Error {
0 commit comments