@@ -28,7 +28,7 @@ var isReadableStream = require('isReadableStream');
2828var rollingAdler32 = require ( "rollingAdler32" ) ;
2929var stream = require ( "stream" ) ;
3030
31- // this is a pass through stream that can calculate the hash that is used to
31+ // this is a pass through stream that can calculate the hash that is used to
3232// checksum react server-rendered elements.
3333class Adler32Stream extends stream . Transform {
3434 constructor ( rootId , options ) {
@@ -73,84 +73,88 @@ class RenderStream extends stream.Readable {
7373 }
7474
7575 _read ( n ) {
76- var bufferToPush ;
77- if ( this . done ) {
78- this . push ( null ) ;
79- return ;
80- }
81- // it's possible that the last chunk added bumped the buffer up to > 2 * n, which means we will
82- // need to go through multiple read calls to drain it down to < n.
83- if ( this . buffer . length >= n ) {
84- bufferToPush = this . buffer . substring ( 0 , n ) ;
85- this . buffer = this . buffer . substring ( n ) ;
86- this . push ( bufferToPush ) ;
87- return ;
88- }
89-
90- if ( this . stream ) {
91- let data = this . stream . read ( n ) ;
92- // if the underlying stream isn't ready, it returns null, so we push a blank string to
93- // get it to work.
94- if ( null === data ) {
95- this . push ( "" ) ;
96- } else {
97- this . push ( data ) ;
76+ try {
77+ var bufferToPush ;
78+ if ( this . done ) {
79+ this . push ( null ) ;
80+ return ;
81+ }
82+ // it's possible that the last chunk added bumped the buffer up to > 2 * n, which means we will
83+ // need to go through multiple read calls to drain it down to < n.
84+ if ( this . buffer . length >= n ) {
85+ bufferToPush = this . buffer . substring ( 0 , n ) ;
86+ this . buffer = this . buffer . substring ( n ) ;
87+ this . push ( bufferToPush ) ;
88+ return ;
9889 }
99- return ;
100- }
101- // if we have are already rendering and have a continuation to call, do so.
102- if ( this . continuation ) {
103- // continue with the rendering.
104- this . continuation ( ) ;
105- return ;
106- }
10790
108- this . stackDepth = 0 ;
109- // start the rendering chain.
110- this . componentInstance . mountComponentAsync ( this . id , this . transaction , this . context ,
111- ( text , cb ) => {
112- if ( isReadableStream ( text ) ) {
113- // this is a stream
114- this . stream = text ;
115- this . stream . on ( "end" , ( ) => {
116- this . stream = null ;
117- cb ( ) ;
118- } ) ;
119- let data = this . stream . read ( n - this . buffer . length ) ;
120-
121- setImmediate ( ( ) => {
122- if ( data === null ) data = this . stream . read ( n - this . buffer . length ) ;
123- this . push ( this . buffer + ( data === null ? "" : data ) ) ;
124- this . buffer = "" ;
125- } ) ;
126- return ;
91+ if ( this . stream ) {
92+ let data = this . stream . read ( n ) ;
93+ // if the underlying stream isn't ready, it returns null, so we push a blank string to
94+ // get it to work.
95+ if ( null === data ) {
96+ this . push ( "" ) ;
97+ } else {
98+ this . push ( data ) ;
12799 }
100+ return ;
101+ }
102+ // if we have are already rendering and have a continuation to call, do so.
103+ if ( this . continuation ) {
104+ // continue with the rendering.
105+ this . continuation ( ) ;
106+ return ;
107+ }
128108
129- this . buffer += text ;
130- if ( this . buffer . length >= n ) {
131- this . continuation = cb ;
132- bufferToPush = this . buffer . substring ( 0 , n ) ;
133- this . buffer = this . buffer . substring ( n ) ;
134- this . push ( bufferToPush ) ;
135- } else {
136- // continue rendering until we have enough text to call this.push().
137- // sometimes do this as process.nextTick to get out of stack overflows.
138- if ( this . stackDepth >= this . maxStackDepth ) {
139- process . nextTick ( cb ) ;
140- } else {
141- this . stackDepth ++ ;
142- cb ( ) ;
143- this . stackDepth -- ;
109+ this . stackDepth = 0 ;
110+ // start the rendering chain.
111+ this . componentInstance . mountComponentAsync ( this . id , this . transaction , this . context ,
112+ ( text , cb ) => {
113+ if ( isReadableStream ( text ) ) {
114+ // this is a stream
115+ this . stream = text ;
116+ this . stream . on ( "end" , ( ) => {
117+ this . stream = null ;
118+ cb ( ) ;
119+ } ) ;
120+ let data = this . stream . read ( n - this . buffer . length ) ;
121+
122+ setImmediate ( ( ) => {
123+ if ( data === null ) data = this . stream . read ( n - this . buffer . length ) ;
124+ this . push ( this . buffer + ( data === null ? "" : data ) ) ;
125+ this . buffer = "" ;
126+ } ) ;
127+ return ;
144128 }
145- }
146- } ,
147- this . cache ,
148- ( ) => {
149- // the rendering is finished; we should push out the last of the buffer.
150- this . done = true ;
151- this . push ( this . buffer ) ;
152- } )
153129
130+ this . buffer += text ;
131+ if ( this . buffer . length >= n ) {
132+ this . continuation = cb ;
133+ bufferToPush = this . buffer . substring ( 0 , n ) ;
134+ this . buffer = this . buffer . substring ( n ) ;
135+ this . push ( bufferToPush ) ;
136+ } else {
137+ // continue rendering until we have enough text to call this.push().
138+ // sometimes do this as process.nextTick to get out of stack overflows.
139+ if ( this . stackDepth >= this . maxStackDepth ) {
140+ process . nextTick ( cb ) ;
141+ } else {
142+ this . stackDepth ++ ;
143+ cb ( ) ;
144+ this . stackDepth -- ;
145+ }
146+ }
147+ } ,
148+ this . cache ,
149+ ( ) => {
150+ // the rendering is finished; we should push out the last of the buffer.
151+ this . done = true ;
152+ this . push ( this . buffer ) ;
153+ } ) ;
154+ } catch ( e ) {
155+ this . emit ( 'error' , e ) ;
156+ return ;
157+ }
154158 }
155159}
156160
@@ -173,20 +177,23 @@ function renderToStringStream(element, {syncBatching = false, cache, rootID} = {
173177 var id = rootID || ReactInstanceHandles . createReactRootID ( ) ;
174178 transaction = ReactServerRenderingTransaction . getPooled ( false ) ;
175179
176- var readable = transaction . perform ( function ( ) {
180+ var markupStream = transaction . perform ( function ( ) {
177181 var componentInstance = instantiateReactComponent ( element , null ) ;
178182 return new RenderStream ( componentInstance , id , transaction , emptyObject , cache ) ;
179183 } , null ) ;
180184
181- readable . on ( "end" , ( ) => {
185+ markupStream . on ( "end" , ( ) => {
182186 ReactServerRenderingTransaction . release ( transaction ) ;
183187 // Revert to the DOM batching strategy since these two renderers
184188 // currently share these stateful modules.
185189 // NOTE: THIS SHOULD ONLY BE DONE IN TESTS OR OTHER ENVIRONMENTS KNOWN TO BE SYNCHRONOUS.
186190 if ( syncBatching ) ReactUpdates . injection . injectBatchingStrategy ( ReactDefaultBatchingStrategy ) ;
187191 } ) ;
188192
189- return readable . pipe ( new Adler32Stream ( id ) ) ;
193+ var checksumedStream = markupStream . pipe ( new Adler32Stream ( id ) ) ;
194+ // manually propagate errors down the chain.
195+ markupStream . on ( 'error' , ( e ) => checksumedStream . emit ( 'error' , e ) ) ;
196+ return checksumedStream ;
190197}
191198
192199/**
0 commit comments