1
+ import { BaseSSRServerContext } from '@modern-js/types' ;
1
2
import { Logger , LoggerInterface } from './libs/logger' ;
2
3
import { ModernRouteInterface , RouteMatchManager } from './libs/route' ;
3
4
import { metrics as defaultMetrics } from './libs/metrics' ;
4
5
5
6
export type Context = Record < string , any > ;
6
7
7
- export interface UrlQuery {
8
- [ key : string ] : string ;
8
+ export interface HandlerOptions {
9
+ request : Request ;
10
+ loadableStats : Record < string , any > ;
11
+ routeManifest : Record < string , any > ;
12
+ }
13
+
14
+ export class ReturnResponse {
15
+ body : string ;
16
+
17
+ status : number ;
18
+
19
+ headers : Headers ;
20
+
21
+ constructor ( body : string , status : number , headers : Record < string , any > = { } ) {
22
+ this . body = body ;
23
+ this . status = status ;
24
+ this . headers = new Headers ( headers ) ;
25
+ this . headers . set ( 'content-type' , 'text/html;charset=UTF-8' ) ;
26
+ }
27
+
28
+ /**
29
+ * Iterate a Object
30
+ * 1. adds the value if the key does not already exist.
31
+ * 2. append the value if the key does already exist.
32
+ *
33
+ * more detail follow: https://developer.mozilla.org/en-US/docs/Web/API/Headers/append
34
+ * @param headers
35
+ * @returns
36
+ */
37
+ appendHeaders ( headers : Record < string , any > ) : this {
38
+ Object . entries ( headers ) . forEach ( ( [ key , value ] ) => {
39
+ this . headers . append ( key , value . toString ( ) as string ) ;
40
+ } ) ;
41
+
42
+ return this ;
43
+ }
44
+
45
+ /**
46
+ * Iterate a Object
47
+ * 1. adds the value if the key does not already exist.
48
+ * 2. modify the value if the key does already exist.
49
+ *
50
+ * more detail follow: https://developer.mozilla.org/en-US/docs/Web/API/Headers/set
51
+ * @param headers
52
+ * @returns
53
+ */
54
+ setHeaders ( headers : Record < string , any > ) : this {
55
+ Object . entries ( headers ) . forEach ( ( [ key , value ] ) => {
56
+ this . headers . set ( key , value . toString ( ) as string ) ;
57
+ } ) ;
58
+
59
+ return this ;
60
+ }
9
61
}
10
62
11
63
export type Manifest = {
@@ -20,67 +72,104 @@ export type Manifest = {
20
72
routes : ModernRouteInterface [ ] ;
21
73
} ;
22
74
23
- export const handleUrl = ( url : string ) => {
24
- return url . replace ( / ^ h t t p s ? : \/ \/ .* ?\/ / gi, '/' ) ;
25
- } ;
75
+ const RESPONSE_NOTFOUND = new ReturnResponse ( '404: Page not found' , 404 ) ;
26
76
27
77
export const createHandler = ( manifest : Manifest ) => {
28
78
const routeMgr = new RouteMatchManager ( ) ;
29
79
const { pages, routes } = manifest ;
30
80
routeMgr . reset ( routes ) ;
31
- return async ( ctx : Context ) => {
32
- const pageMatch = routeMgr . match ( ctx . url ) ;
81
+ return async ( options : HandlerOptions ) : Promise < ReturnResponse > => {
82
+ const { request, loadableStats, routeManifest } = options ;
83
+ // eslint-disable-next-line node/no-unsupported-features/node-builtins, node/prefer-global/url
84
+ const url = new URL ( request . url ) ;
85
+ const pageMatch = routeMgr . match ( url . pathname ) ;
33
86
if ( ! pageMatch ) {
34
- ctx . body = '404: Page not found' ;
35
- ctx . status = 404 ;
36
- return ;
87
+ return RESPONSE_NOTFOUND ;
37
88
}
38
89
const page = pages [ pageMatch . spec . urlPath ] ;
39
- ctx . request . query ??= ctx . query ;
40
- ctx . request . pathname ??= ctx . pathname ;
41
- ctx . request . params ??= ctx . params ;
42
- const params = pageMatch . parseURLParams ( ctx . url ) ;
43
90
if ( page . serverRender ) {
44
91
try {
45
- ctx . body = await page . serverRender ( {
46
- entryName : page . entryName ,
92
+ const responseLike = {
93
+ headers : { } as Record < string , any > ,
94
+ statusCode : 200 ,
95
+ locals : { } as Record < string , any > ,
96
+ setHeader ( key : string , value : string ) {
97
+ this . headers [ key ] = value ;
98
+ } ,
99
+ status ( code : number ) {
100
+ this . statusCode = code ;
101
+ } ,
102
+ } ;
103
+ const params = pageMatch . parseURLParams ( url . pathname ) || { } ;
104
+
105
+ const serverRenderContext : BaseSSRServerContext = {
106
+ request : createServerRequest ( url , request , params ) ,
107
+ response : responseLike ,
108
+ loadableStats,
109
+ routeManifest,
110
+ redirection : { } ,
47
111
template : page . template ,
48
- query : ctx . query ,
49
- request : ctx . request ,
50
- response : ctx . response ,
51
- pathname : ctx . pathname ,
52
- req : ctx . request ,
53
- res : ctx . response ,
54
- params : ctx . params || params || { } ,
55
- logger :
56
- ctx . logger ||
57
- ( new Logger ( {
58
- level : 'warn' ,
59
- } ) as Logger & LoggerInterface ) ,
60
- metrics : ctx . metrics || defaultMetrics ,
61
- loadableStats : ctx . loadableStats ,
62
- routeManifest : ctx . routeManifest ,
63
- } ) ;
64
- ctx . status = 200 ;
65
- return ;
112
+ entryName : page . entryName ,
113
+ logger : new Logger ( {
114
+ level : 'warn' ,
115
+ } ) as Logger & LoggerInterface ,
116
+ metrics : defaultMetrics as any ,
117
+ // FIXME: pass correctly req & res
118
+ req : request as any ,
119
+ res : responseLike as any ,
120
+ } ;
121
+
122
+ const body = await page . serverRender ( serverRenderContext ) ;
123
+
124
+ return new ReturnResponse (
125
+ body ,
126
+ responseLike . statusCode ,
127
+ responseLike . headers ,
128
+ ) ;
66
129
} catch ( e ) {
67
- if ( page . template ) {
68
- ctx . body = page . template ;
69
- ctx . status = 200 ;
70
- return ;
71
- } else {
72
- ctx . body = '404: not found' ;
73
- ctx . status = 404 ;
74
- return ;
75
- }
130
+ console . warn (
131
+ `page(${ pageMatch . spec . urlPath } ) serverRender occur error: ` ,
132
+ ) ;
133
+ console . warn ( e ) ;
134
+
135
+ return createResponse ( page . template ) ;
76
136
}
77
137
}
78
- if ( page . template ) {
79
- ctx . body = page . template ;
80
- ctx . status = 200 ;
81
- return ;
138
+
139
+ console . warn ( `Can't not page(${ pageMatch . spec . urlPath } ) serverRender` ) ;
140
+
141
+ return createResponse ( page . template ) ;
142
+
143
+ function createServerRequest (
144
+ // eslint-disable-next-line node/no-unsupported-features/node-builtins, node/prefer-global/url
145
+ url : URL ,
146
+ request : Request ,
147
+ params : Record < string , string > ,
148
+ ) {
149
+ const { pathname, host, searchParams } = url ;
150
+ const { headers : rawHeaders } = request ;
151
+ const headers = { } as Record < string , any > ;
152
+ rawHeaders . forEach ( ( value , key ) => {
153
+ headers [ key ] = value ;
154
+ } ) ;
155
+ // eslint-disable-next-line node/no-unsupported-features/es-builtins
156
+ const query = Object . fromEntries ( searchParams ) ;
157
+
158
+ return {
159
+ pathname,
160
+ host,
161
+ headers,
162
+ params,
163
+ query,
164
+ } ;
82
165
}
83
- ctx . body = '404: not found' ;
84
- ctx . status = 404 ;
85
166
} ;
86
167
} ;
168
+
169
+ function createResponse ( template ?: string ) {
170
+ if ( template ) {
171
+ return new ReturnResponse ( template , 200 ) ;
172
+ } else {
173
+ return RESPONSE_NOTFOUND ;
174
+ }
175
+ }
0 commit comments