@@ -19,6 +19,7 @@ type Assets = {
19
19
}
20
20
21
21
type RouteObject = {
22
+ id : string ;
22
23
type : 'page' | 'route' ;
23
24
pattern : RegExp ;
24
25
params : ( match : RegExpMatchArray ) => Record < string , string > ;
@@ -93,9 +94,7 @@ export default function middleware({ routes }: {
93
94
}
94
95
} ,
95
96
96
- get_route_handler ( client_info . assetsByChunkName , routes , template ) ,
97
-
98
- get_not_found_handler ( client_info . assetsByChunkName , routes , template )
97
+ get_route_handler ( client_info . assetsByChunkName , routes , template )
99
98
] . filter ( Boolean ) ) ;
100
99
101
100
return middleware ;
@@ -119,13 +118,12 @@ function get_asset_handler({ pathname, type, cache, body }: {
119
118
const resolved = Promise . resolve ( ) ;
120
119
121
120
function get_route_handler ( chunks : Record < string , string > , routes : RouteObject [ ] , template : Template ) {
122
- function handle_route ( route : RouteObject , req : Req , res : ServerResponse , next : ( ) => void ) {
121
+ function handle_route ( route : RouteObject , req : Req , res : ServerResponse ) {
123
122
req . params = route . params ( route . pattern . exec ( req . pathname ) ) ;
124
123
125
124
const mod = route . module ;
126
125
127
126
if ( route . type === 'page' ) {
128
- // for page routes, we're going to serve some HTML
129
127
res . setHeader ( 'Content-Type' , 'text/html' ) ;
130
128
131
129
// preload main.js and current route
@@ -134,33 +132,44 @@ function get_route_handler(chunks: Record<string, string>, routes: RouteObject[]
134
132
135
133
const data = { params : req . params , query : req . query } ;
136
134
137
- if ( mod . preload ) {
138
- const promise = Promise . resolve ( mod . preload ( req ) ) . then ( preloaded => {
139
- const serialized = try_serialize ( preloaded ) ;
140
- Object . assign ( data , preloaded ) ;
141
-
142
- return { rendered : mod . render ( data ) , serialized } ;
143
- } ) ;
135
+ let redirect : { statusCode : number , location : string } ;
136
+ let error : { statusCode : number , message : Error | string } ;
137
+
138
+ Promise . resolve (
139
+ mod . preload ? mod . preload . call ( {
140
+ redirect : ( statusCode : number , location : string ) => {
141
+ redirect = { statusCode, location } ;
142
+ } ,
143
+ error : ( statusCode : number , message : Error | string ) => {
144
+ error = { statusCode, message } ;
145
+ }
146
+ } , req ) : { }
147
+ ) . catch ( err => {
148
+ error = { statusCode : 500 , message : err } ;
149
+ } ) . then ( preloaded => {
150
+ if ( redirect ) {
151
+ res . statusCode = redirect . statusCode ;
152
+ res . setHeader ( 'Location' , redirect . location ) ;
153
+ res . end ( ) ;
154
+
155
+ return ;
156
+ }
144
157
145
- return template . stream ( req , res , {
146
- scripts : promise . then ( ( { serialized } ) => {
147
- const main = `<script src='/client/${ chunks . main } '></script>` ;
158
+ if ( error ) {
159
+ handle_error ( req , res , error . statusCode , error . message ) ;
160
+ return ;
161
+ }
148
162
149
- if ( serialized ) {
150
- return `<script>__SAPPER__ = { preloaded: ${ serialized } };</script>${ main } ` ;
151
- }
163
+ const serialized = try_serialize ( preloaded ) ; // TODO bail on non-POJOs
164
+ Object . assign ( data , preloaded ) ;
152
165
153
- return main ;
154
- } ) ,
155
- html : promise . then ( ( { rendered } ) => rendered . html ) ,
156
- head : promise . then ( ( { rendered } ) => `<noscript id='sapper-head-start'></noscript>${ rendered . head } <noscript id='sapper-head-end'></noscript>` ) ,
157
- styles : promise . then ( ( { rendered } ) => ( rendered . css && rendered . css . code ? `<style>${ rendered . css . code } </style>` : '' ) )
158
- } ) ;
159
- } else {
160
166
const { html, head, css } = mod . render ( data ) ;
161
167
168
+ let scripts = `<script src='/client/${ chunks . main } '></script>` ;
169
+ scripts = `<script>__SAPPER__ = { preloaded: ${ serialized } };</script>${ scripts } ` ;
170
+
162
171
const page = template . render ( {
163
- scripts : `<script src='/client/ ${ chunks . main } '></script>` ,
172
+ scripts,
164
173
html,
165
174
head : `<noscript id='sapper-head-start'></noscript>${ head } <noscript id='sapper-head-end'></noscript>` ,
166
175
styles : ( css && css . code ? `<style>${ css . code } </style>` : '' )
@@ -178,7 +187,7 @@ function get_route_handler(chunks: Record<string, string>, routes: RouteObject[]
178
187
body : page
179
188
} ) ;
180
189
}
181
- }
190
+ } ) ;
182
191
}
183
192
184
193
else {
@@ -219,60 +228,55 @@ function get_route_handler(chunks: Record<string, string>, routes: RouteObject[]
219
228
} ;
220
229
}
221
230
222
- handler ( req , res , next ) ;
231
+ handler ( req , res , ( ) => {
232
+ handle_not_found ( req , res , 404 , 'Not found' ) ;
233
+ } ) ;
223
234
} else {
224
235
// no matching handler for method — 404
225
- next ( ) ;
236
+ handle_not_found ( req , res , 404 , 'Not found' ) ;
226
237
}
227
238
}
228
239
}
229
240
230
- const error_route = routes . find ( ( route : RouteObject ) => route . error === '5xx' )
241
+ const not_found_route = routes . find ( ( route : RouteObject ) => route . error === '4xx' ) ;
231
242
232
- return function find_route ( req : Req , res : ServerResponse , next : ( ) => void ) {
233
- const url = req . pathname ;
243
+ function handle_not_found ( req : Req , res : ServerResponse , statusCode : number , message : Error | string ) {
244
+ res . statusCode = statusCode ;
245
+ res . setHeader ( 'Content-Type' , 'text/html' ) ;
234
246
235
- try {
236
- for ( const route of routes ) {
237
- if ( ! route . error && route . pattern . test ( url ) ) return handle_route ( route , req , res , next ) ;
238
- }
247
+ const error = message instanceof Error ? message : new Error ( message ) ;
239
248
240
- // no matching route — 404
241
- next ( ) ;
242
- } catch ( error ) {
243
- console . error ( error ) ;
249
+ const rendered = not_found_route ? not_found_route . module . render ( {
250
+ status : 404 ,
251
+ error
252
+ } ) : { head : '' , css : null , html : error . message } ;
244
253
245
- res . statusCode = 500 ;
246
- res . setHeader ( 'Content-Type' , 'text/html' ) ;
254
+ const { head, css, html } = rendered ;
247
255
248
- const rendered = error_route ? error_route . module . render ( {
249
- status : 500 ,
250
- error
251
- } ) : { head : '' , css : null , html : 'Not found' } ;
256
+ res . end ( template . render ( {
257
+ scripts : `<script src='/client/${ chunks . main } '></script>` ,
258
+ html,
259
+ head : `<noscript id='sapper-head-start'></noscript>${ head } <noscript id='sapper-head-end'></noscript>` ,
260
+ styles : ( css && css . code ? `<style>${ css . code } </style>` : '' )
261
+ } ) ) ;
262
+ }
252
263
253
- const { head , css , html } = rendered ;
264
+ const error_route = routes . find ( ( route : RouteObject ) => route . error === '5xx' ) ;
254
265
255
- res . end ( template . render ( {
256
- scripts : `<script src='/client/${ chunks . main } '></script>` ,
257
- html,
258
- head : `<noscript id='sapper-head-start'></noscript>${ head } <noscript id='sapper-head-end'></noscript>` ,
259
- styles : ( css && css . code ? `<style>${ css . code } </style>` : '' )
260
- } ) ) ;
266
+ function handle_error ( req : Req , res : ServerResponse , statusCode : number , message : Error | string ) {
267
+ if ( statusCode >= 400 && statusCode < 500 ) {
268
+ return handle_not_found ( req , res , statusCode , message ) ;
261
269
}
262
- } ;
263
- }
264
270
265
- function get_not_found_handler ( chunks : Record < string , string > , routes : RouteObject [ ] , template : Template ) {
266
- const route = routes . find ( ( route : RouteObject ) => route . error === '4xx' ) ;
267
-
268
- return function handle_not_found ( req : Req , res : ServerResponse ) {
269
- res . statusCode = 404 ;
271
+ res . statusCode = statusCode ;
270
272
res . setHeader ( 'Content-Type' , 'text/html' ) ;
271
273
272
- const rendered = route ? route . module . render ( {
273
- status : 404 ,
274
- message : 'Not found'
275
- } ) : { head : '' , css : null , html : 'Not found' } ;
274
+ const error = message instanceof Error ? message : new Error ( message ) ;
275
+
276
+ const rendered = error_route ? error_route . module . render ( {
277
+ status : 500 ,
278
+ error
279
+ } ) : { head : '' , css : null , html : `Internal server error: ${ error . message } ` } ;
276
280
277
281
const { head, css, html } = rendered ;
278
282
@@ -282,6 +286,20 @@ function get_not_found_handler(chunks: Record<string, string>, routes: RouteObje
282
286
head : `<noscript id='sapper-head-start'></noscript>${ head } <noscript id='sapper-head-end'></noscript>` ,
283
287
styles : ( css && css . code ? `<style>${ css . code } </style>` : '' )
284
288
} ) ) ;
289
+ }
290
+
291
+ return function find_route ( req : Req , res : ServerResponse , next : ( ) => void ) {
292
+ const url = req . pathname ;
293
+
294
+ try {
295
+ for ( const route of routes ) {
296
+ if ( ! route . error && route . pattern . test ( url ) ) return handle_route ( route , req , res ) ;
297
+ }
298
+
299
+ handle_not_found ( req , res , 404 , 'Not found' ) ;
300
+ } catch ( error ) {
301
+ handle_error ( req , res , 500 , error ) ;
302
+ }
285
303
} ;
286
304
}
287
305
0 commit comments