@@ -52,97 +52,54 @@ async function analyse({ manifest_path, env }) {
52
52
routes : new Map ( )
53
53
} ;
54
54
55
- // analyse nodes
56
- for ( const loader of manifest . _ . nodes ) {
57
- const node = await loader ( ) ;
55
+ const nodes = await Promise . all ( manifest . _ . nodes . map ( ( loader ) => loader ( ) ) ) ;
58
56
57
+ // analyse nodes
58
+ for ( const node of nodes ) {
59
59
metadata . nodes [ node . index ] = {
60
60
has_server_load : node . server ?. load !== undefined || node . server ?. trailingSlash !== undefined
61
61
} ;
62
62
}
63
63
64
64
// analyse routes
65
65
for ( const route of manifest . _ . routes ) {
66
- /** @type {Array<'GET' | 'POST'> } */
67
- const page_methods = [ ] ;
68
-
69
- /** @type {(import('types').HttpMethod | '*')[] } */
70
- const api_methods = [ ] ;
66
+ const page =
67
+ route . page &&
68
+ analyse_page (
69
+ route . page . layouts . map ( ( n ) => ( n === undefined ? n : nodes [ n ] ) ) ,
70
+ nodes [ route . page . leaf ]
71
+ ) ;
71
72
72
- /** @type {import('types').PrerenderOption | undefined } */
73
- let prerender = undefined ;
74
- /** @type {any } */
75
- let config = undefined ;
76
- /** @type {import('types').PrerenderEntryGenerator | undefined } */
77
- let entries = undefined ;
73
+ const endpoint = route . endpoint && analyse_endpoint ( route , await route . endpoint ( ) ) ;
78
74
79
- if ( route . endpoint ) {
80
- const mod = await route . endpoint ( ) ;
81
- if ( mod . prerender !== undefined ) {
82
- validate_server_exports ( mod , route . id ) ;
75
+ if ( page ?. prerender && endpoint ?. prerender ) {
76
+ throw new Error ( `Cannot prerender a route with both +page and +server files (${ route . id } )` ) ;
77
+ }
83
78
84
- if ( mod . prerender && ( mod . POST || mod . PATCH || mod . PUT || mod . DELETE ) ) {
79
+ if ( page ?. config && endpoint ?. config ) {
80
+ for ( const key in { ...page . config , ...endpoint . config } ) {
81
+ if ( JSON . stringify ( page . config [ key ] ) !== JSON . stringify ( endpoint . config [ key ] ) ) {
85
82
throw new Error (
86
- `Cannot prerender a + server file with POST, PATCH, PUT, or DELETE ( ${ route . id } ) `
83
+ `Mismatched route config for ${ route . id } — the +page and + server files must export the same config, if any `
87
84
) ;
88
85
}
89
-
90
- prerender = mod . prerender ;
91
86
}
92
-
93
- Object . values ( mod ) . forEach ( ( /** @type {import('types').HttpMethod } */ method ) => {
94
- if ( mod [ method ] && ENDPOINT_METHODS . has ( method ) ) {
95
- api_methods . push ( method ) ;
96
- } else if ( mod . fallback ) {
97
- api_methods . push ( '*' ) ;
98
- }
99
- } ) ;
100
-
101
- config = mod . config ;
102
- entries = mod . entries ;
103
87
}
104
88
105
- if ( route . page ) {
106
- const nodes = await Promise . all (
107
- [ ...route . page . layouts , route . page . leaf ] . map ( ( n ) => {
108
- if ( n !== undefined ) return manifest . _ . nodes [ n ] ( ) ;
109
- } )
110
- ) ;
111
-
112
- const layouts = nodes . slice ( 0 , - 1 ) ;
113
- const page = nodes . at ( - 1 ) ;
114
-
115
- for ( const layout of layouts ) {
116
- if ( layout ) {
117
- validate_layout_server_exports ( layout . server , layout . server_id ) ;
118
- validate_layout_exports ( layout . universal , layout . universal_id ) ;
119
- }
120
- }
121
-
122
- if ( page ) {
123
- page_methods . push ( 'GET' ) ;
124
- if ( page . server ?. actions ) page_methods . push ( 'POST' ) ;
125
-
126
- validate_page_server_exports ( page . server , page . server_id ) ;
127
- validate_page_exports ( page . universal , page . universal_id ) ;
128
- }
129
-
130
- prerender = get_option ( nodes , 'prerender' ) ?? false ;
131
-
132
- config = get_config ( nodes ) ;
133
- entries ??= get_option ( nodes , 'entries' ) ;
134
- }
89
+ const page_methods = page ?. methods ?? [ ] ;
90
+ const api_methods = endpoint ?. methods ?? [ ] ;
91
+ const entries = page ?. entries ?? endpoint ?. entries ;
135
92
136
93
metadata . routes . set ( route . id , {
137
- config,
94
+ config : page ?. config ?? endpoint ?. config ,
138
95
methods : Array . from ( new Set ( [ ...page_methods , ...api_methods ] ) ) ,
139
96
page : {
140
97
methods : page_methods
141
98
} ,
142
99
api : {
143
100
methods : api_methods
144
101
} ,
145
- prerender,
102
+ prerender : page ?. prerender ?? endpoint ?. prerender ,
146
103
entries :
147
104
entries && ( await entries ( ) ) . map ( ( entry_object ) => resolvePath ( route . id , entry_object ) )
148
105
} ) ;
@@ -151,20 +108,81 @@ async function analyse({ manifest_path, env }) {
151
108
return metadata ;
152
109
}
153
110
111
+ /**
112
+ * @param {import('types').SSRRoute } route
113
+ * @param {import('types').SSREndpoint } mod
114
+ */
115
+ function analyse_endpoint ( route , mod ) {
116
+ validate_server_exports ( mod , route . id ) ;
117
+
118
+ if ( mod . prerender && ( mod . POST || mod . PATCH || mod . PUT || mod . DELETE ) ) {
119
+ throw new Error (
120
+ `Cannot prerender a +server file with POST, PATCH, PUT, or DELETE (${ route . id } )`
121
+ ) ;
122
+ }
123
+
124
+ /** @type {Array<import('types').HttpMethod | '*'> } */
125
+ const methods = [ ] ;
126
+
127
+ Object . values ( mod ) . forEach ( ( /** @type {import('types').HttpMethod } */ method ) => {
128
+ if ( mod [ method ] && ENDPOINT_METHODS . has ( method ) ) {
129
+ methods . push ( method ) ;
130
+ } else if ( mod . fallback ) {
131
+ methods . push ( '*' ) ;
132
+ }
133
+ } ) ;
134
+
135
+ return {
136
+ config : mod . config ,
137
+ entries : mod . entries ,
138
+ methods,
139
+ prerender : mod . prerender ?? false
140
+ } ;
141
+ }
142
+
143
+ /**
144
+ * @param {Array<import('types').SSRNode | undefined> } layouts
145
+ * @param {import('types').SSRNode } leaf
146
+ */
147
+ function analyse_page ( layouts , leaf ) {
148
+ for ( const layout of layouts ) {
149
+ if ( layout ) {
150
+ validate_layout_server_exports ( layout . server , layout . server_id ) ;
151
+ validate_layout_exports ( layout . universal , layout . universal_id ) ;
152
+ }
153
+ }
154
+
155
+ /** @type {Array<'GET' | 'POST'> } */
156
+ const methods = [ 'GET' ] ;
157
+ if ( leaf . server ?. actions ) methods . push ( 'POST' ) ;
158
+
159
+ validate_page_server_exports ( leaf . server , leaf . server_id ) ;
160
+ validate_page_exports ( leaf . universal , leaf . universal_id ) ;
161
+
162
+ return {
163
+ config : get_config ( [ ...layouts , leaf ] ) ,
164
+ entries : leaf . universal ?. entries ?? leaf . server ?. entries ,
165
+ methods,
166
+ prerender : get_option ( [ ...layouts , leaf ] , 'prerender' ) ?? false
167
+ } ;
168
+ }
169
+
154
170
/**
155
171
* Do a shallow merge (first level) of the config object
156
172
* @param {Array<import('types').SSRNode | undefined> } nodes
157
173
*/
158
174
function get_config ( nodes ) {
175
+ /** @type {any } */
159
176
let current = { } ;
177
+
160
178
for ( const node of nodes ) {
161
- const config = node ?. universal ?. config ?? node ?. server ?. config ;
162
- if ( config ) {
163
- current = {
164
- ...current ,
165
- ...config
166
- } ;
167
- }
179
+ if ( ! node ?. universal ?. config && ! node ?. server ?. config ) continue ;
180
+
181
+ current = {
182
+ ...current ,
183
+ ...node ?. universal ?. config ,
184
+ ... node ?. server ?. config
185
+ } ;
168
186
}
169
187
170
188
return Object . keys ( current ) . length ? current : undefined ;
0 commit comments