@@ -2,13 +2,17 @@ import { FileSystemRouter } from "bun";
2
2
import { NJSON } from "next-json" ;
3
3
import { statSync } from "node:fs" ;
4
4
import { join , relative } from "node:path" ;
5
+ import { preloadModule } from "react-dom" ;
5
6
import { renderToReadableStream } from "react-dom/server" ;
6
7
import { ClientOnlyError } from "./client" ;
8
+ import { MetaContext , PreloadModule } from "./preload" ;
7
9
8
10
export class StaticRouters {
9
11
readonly server : FileSystemRouter ;
10
12
readonly client : FileSystemRouter ;
13
+ readonly #routes: Map < string , string > ;
11
14
readonly #routes_dump: string ;
15
+ readonly #dependencies: Record < string , string [ ] > ;
12
16
readonly #hashed: Record < string , string > ;
13
17
14
18
constructor (
@@ -24,17 +28,19 @@ export class StaticRouters {
24
28
dir : join ( baseDir , buildDir , pageDir ) ,
25
29
style : "nextjs" ,
26
30
} ) ;
27
- this . #hashed = require ( join ( baseDir , buildDir , ".meta.json" ) ) . hashed ;
28
- this . #routes_dump = NJSON . stringify (
29
- Object . fromEntries (
30
- Object . entries ( this . client . routes ) . map ( ( [ path , filePath ] ) => {
31
- let target = "/" + relative ( join ( baseDir , buildDir ) , filePath ) ;
32
- if ( this . #hashed[ target ] ) target += `?${ this . #hashed[ target ] } ` ;
33
- return [ path , target ] ;
34
- } )
35
- ) ,
36
- { omitStack : true }
31
+ const parsed = require ( join ( baseDir , buildDir , ".meta.json" ) ) ;
32
+ this . #hashed = parsed . hashed ;
33
+ this . #dependencies = parsed . dependencies ;
34
+ this . #routes = new Map (
35
+ Object . entries ( this . client . routes ) . map ( ( [ path , filePath ] ) => {
36
+ let target = "/" + relative ( join ( baseDir , buildDir ) , filePath ) ;
37
+ if ( this . #hashed[ target ] ) target += `?${ this . #hashed[ target ] } ` ;
38
+ return [ path , target ] ;
39
+ } )
37
40
) ;
41
+ this . #routes_dump = NJSON . stringify ( Object . fromEntries ( this . #routes) , {
42
+ omitStack : true ,
43
+ } ) ;
38
44
}
39
45
40
46
async serve < T = void > (
@@ -101,7 +107,14 @@ export class StaticRouters {
101
107
}
102
108
const stream = await renderToReadableStream (
103
109
< Shell route = { serverSide . pathname + search } { ...staticProps } { ...result } >
104
- < module . default { ...result ?. props } />
110
+ < MetaContext . Provider
111
+ value = { { hash : this . #hashed, dependencies : this . #dependencies } }
112
+ >
113
+ < PreloadModule
114
+ module = { this . #routes. get ( serverSide . pathname ) ! . split ( "?" ) [ 0 ] }
115
+ />
116
+ < module . default { ...result ?. props } />
117
+ </ MetaContext . Provider >
105
118
</ Shell > ,
106
119
{
107
120
signal : request . signal ,
@@ -140,6 +153,34 @@ export class StaticRouters {
140
153
}
141
154
}
142
155
156
+ function DirectPreloadModule ( {
157
+ target,
158
+ dependencies,
159
+ } : {
160
+ target : string ;
161
+ dependencies : Record < string , string [ ] > ;
162
+ } ) {
163
+ preloadModule ( target , { as : "script" } ) ;
164
+ preloadModule ( target , { as : "script" } ) ;
165
+ for ( const dep of walkDependencies ( target , dependencies ) ) {
166
+ preloadModule ( dep , { as : "script" } ) ;
167
+ preloadModule ( dep , { as : "script" } ) ;
168
+ }
169
+ return null ;
170
+ }
171
+
172
+ function * walkDependencies (
173
+ target : string ,
174
+ dependencies : Record < string , string [ ] >
175
+ ) : Generator < string > {
176
+ if ( dependencies [ target ] ) {
177
+ for ( const dep of dependencies [ target ] ) {
178
+ yield dep ;
179
+ yield * walkDependencies ( dep , dependencies ) ;
180
+ }
181
+ }
182
+ }
183
+
143
184
export async function serveFromDir ( config : {
144
185
directory : string ;
145
186
path : string ;
0 commit comments