@@ -4,23 +4,22 @@ import { read } from '../../../utils/filesystem.js';
4
4
5
5
const inheritable_page_options = new Set ( [ 'ssr' , 'prerender' , 'csr' , 'trailingSlash' , 'config' ] ) ;
6
6
7
- const page_options = new Set ( [ ...inheritable_page_options , 'entries' ] ) ;
7
+ const valid_page_options = new Set ( [ ...inheritable_page_options , 'entries' ] ) ;
8
8
9
9
const skip_parsing_regex = new RegExp (
10
- `${ Array . from ( page_options ) . join ( '|' ) } |(?:export[\\s\\n]+\\*[\\s\\n]+from)`
10
+ `${ Array . from ( valid_page_options ) . join ( '|' ) } |(?:export[\\s\\n]+\\*[\\s\\n]+from)`
11
11
) ;
12
12
13
13
const parser = Parser . extend ( tsPlugin ( ) ) ;
14
14
15
15
/**
16
- * Collects exported page options from a +page.js/+layout.js file.
17
- * We ignore reassignments and use the declared value.
18
- * Returns `null` if any export is too difficult to analyse.
19
- * @param {string } filename
16
+ * Collects page options from a +page.js/+layout.js file, ignoring reassignments
17
+ * and using the declared value. Returns `null` if any export is too difficult to analyse.
18
+ * @param {string } filename The name of the file to report when an error occurs
20
19
* @param {string } input
21
20
* @returns {Record<string, any> | null }
22
21
*/
23
- export function statically_analyse_exports ( filename , input ) {
22
+ export function statically_analyse_page_options ( filename , input ) {
24
23
// if there's a chance there are no page exports or export all declaration,
25
24
// then we can skip the AST parsing which is expensive
26
25
if ( ! skip_parsing_regex . test ( input ) ) {
@@ -34,14 +33,14 @@ export function statically_analyse_exports(filename, input) {
34
33
} ) ;
35
34
36
35
/** @type {Map<string, import('acorn').Literal['value']> } */
37
- const static_exports = new Map ( ) ;
36
+ const page_options = new Map ( ) ;
38
37
39
38
for ( const statement of source . body ) {
40
39
// ignore export all declarations with aliases that are not page options
41
40
if (
42
41
statement . type === 'ExportAllDeclaration' &&
43
42
statement . exported &&
44
- ! page_options . has ( get_name ( statement . exported ) )
43
+ ! valid_page_options . has ( get_name ( statement . exported ) )
45
44
) {
46
45
continue ;
47
46
}
@@ -60,7 +59,7 @@ export function statically_analyse_exports(filename, input) {
60
59
const export_specifiers = new Map ( ) ;
61
60
for ( const specifier of statement . specifiers ) {
62
61
const exported_name = get_name ( specifier . exported ) ;
63
- if ( ! page_options . has ( exported_name ) ) {
62
+ if ( ! valid_page_options . has ( exported_name ) ) {
64
63
continue ;
65
64
}
66
65
@@ -109,7 +108,7 @@ export function statically_analyse_exports(filename, input) {
109
108
}
110
109
111
110
if ( variable_declarator . init ?. type === 'Literal' ) {
112
- static_exports . set (
111
+ page_options . set (
113
112
/** @type {string } */ ( export_specifiers . get ( variable_declarator . id . name ) ) ,
114
113
variable_declarator . init . value
115
114
) ;
@@ -138,7 +137,7 @@ export function statically_analyse_exports(filename, input) {
138
137
139
138
// class and function declarations
140
139
if ( statement . declaration . type !== 'VariableDeclaration' ) {
141
- if ( page_options . has ( statement . declaration . id . name ) ) {
140
+ if ( valid_page_options . has ( statement . declaration . id . name ) ) {
142
141
return null ;
143
142
}
144
143
continue ;
@@ -149,12 +148,12 @@ export function statically_analyse_exports(filename, input) {
149
148
return null ;
150
149
}
151
150
152
- if ( ! page_options . has ( declaration . id . name ) ) {
151
+ if ( ! valid_page_options . has ( declaration . id . name ) ) {
153
152
continue ;
154
153
}
155
154
156
155
if ( declaration . init ?. type === 'Literal' ) {
157
- static_exports . set ( declaration . id . name , declaration . init . value ) ;
156
+ page_options . set ( declaration . id . name , declaration . init . value ) ;
158
157
continue ;
159
158
}
160
159
@@ -163,10 +162,10 @@ export function statically_analyse_exports(filename, input) {
163
162
}
164
163
}
165
164
166
- return Object . fromEntries ( static_exports ) ;
165
+ return Object . fromEntries ( page_options ) ;
167
166
} catch ( error ) {
168
167
if ( error instanceof Error ) {
169
- error . message = `Failed to statically analyse ${ filename } . ${ error . message } ` ;
168
+ error . message = `Failed to statically analyse page options for ${ filename } . ${ error . message } ` ;
170
169
}
171
170
throw error ;
172
171
}
@@ -183,77 +182,82 @@ export function get_name(node) {
183
182
/**
184
183
* @param {{
185
184
* resolve: (file: string) => Promise<Record<string, any>>;
186
- * static_exports?: Map<string, Record<string, any> | null>;
185
+ * static_exports?: Map<string, { page_options: Record<string, any> | null, children: string[] } >;
187
186
* }} opts
188
187
*/
189
188
export function create_node_analyser ( { resolve, static_exports = new Map ( ) } ) {
190
189
/**
191
190
* Computes the final page options for a node (if possible). Otherwise, returns `null`.
192
191
* @param {import('types').PageNode } node
193
- * @returns {Promise<import('types').UniversalNode | null> }
192
+ * @returns {Promise<Record<string, any> | null> }
194
193
*/
195
194
const get_page_options = async ( node ) => {
196
- if ( node . universal && static_exports . has ( node . universal ) ) {
197
- return /** @type {import('types').UniversalNode | null } */ (
198
- static_exports . get ( node . universal )
199
- ) ;
195
+ const key = node . universal || node . server ;
196
+ if ( key && static_exports . has ( key ) ) {
197
+ return { ...static_exports . get ( key ) ?. page_options } ;
200
198
}
201
199
202
- /** @type {Record<string, any> | null } */
200
+ /** @type {Record<string, any> } */
203
201
let page_options = { } ;
204
202
205
- if ( node . server ) {
206
- const module = await resolve ( node . server ) ;
207
- for ( const key in inheritable_page_options ) {
208
- if ( key in module ) {
209
- page_options [ key ] = module [ key ] ;
210
- }
211
- }
212
- }
203
+ if ( node . parent ) {
204
+ const parent_options = await get_page_options ( node . parent ) ;
213
205
214
- if ( node . universal ) {
215
- let universal_exports = static_exports . get ( node . universal ) ;
216
- if ( universal_exports === undefined ) {
217
- const input = read ( node . universal ) ;
218
- universal_exports = statically_analyse_exports ( node . universal , input ) ;
206
+ const parent_key = node . parent . universal || node . parent . server ;
207
+ if ( key && parent_key ) {
208
+ static_exports . get ( parent_key ) ?. children . push ( key ) ;
219
209
}
220
210
221
- if ( universal_exports === null ) {
222
- static_exports . set ( node . universal , null ) ;
211
+ if ( parent_options === null ) {
212
+ // if the parent cannot be analysed, we can't know what page options
213
+ // the child node inherits, so we also mark it as unanalysable
214
+ if ( key ) {
215
+ static_exports . set ( key , { page_options : null , children : [ ] } ) ;
216
+ }
223
217
return null ;
224
218
}
225
219
226
- page_options = { ...page_options , ... universal_exports } ;
220
+ page_options = { ...parent_options } ;
227
221
}
228
222
229
- if ( node . parent ) {
230
- const parent_options = await get_page_options ( node . parent ) ;
231
- if ( parent_options === null ) {
232
- // if the parent cannot be statically analysed, we can't know what
233
- // page options the current node inherits, so we invalidate it too
234
- if ( node . universal ) {
235
- static_exports . set ( node . universal , null ) ;
223
+ if ( node . server ) {
224
+ const module = await resolve ( node . server ) ;
225
+ for ( const page_option in inheritable_page_options ) {
226
+ if ( page_option in module ) {
227
+ page_options [ page_option ] = module [ page_option ] ;
236
228
}
229
+ }
230
+ }
231
+
232
+ if ( node . universal ) {
233
+ const input = read ( node . universal ) ;
234
+ const universal_page_options = statically_analyse_page_options ( node . universal , input ) ;
235
+
236
+ if ( universal_page_options === null ) {
237
+ static_exports . set ( node . universal , { page_options : null , children : [ ] } ) ;
237
238
return null ;
238
239
}
239
240
240
- page_options = { ...parent_options , ...page_options } ;
241
+ page_options = { ...page_options , ...universal_page_options } ;
241
242
}
242
243
243
- if ( node . universal ) {
244
- static_exports . set ( node . universal , page_options ) ;
244
+ if ( key ) {
245
+ static_exports . set ( key , { page_options, children : [ ] } ) ;
245
246
}
246
247
247
248
return page_options ;
248
249
} ;
249
250
250
251
/**
251
252
* @param {string } file
252
- * @returns {void }
253
253
*/
254
254
const invalidate_page_options = ( file ) => {
255
+ static_exports . get ( file ) ?. children . forEach ( ( child ) => static_exports . delete ( child ) ) ;
255
256
static_exports . delete ( file ) ;
256
257
} ;
257
258
258
- return { get_page_options, invalidate_page_options } ;
259
+ return {
260
+ get_page_options,
261
+ invalidate_page_options
262
+ } ;
259
263
}
0 commit comments