@@ -2,9 +2,9 @@ use std::io::Write;
22
33use anyhow:: Result ;
44use either:: Either ;
5- use indoc:: writedoc ;
5+ use indoc:: formatdoc ;
66use serde:: Serialize ;
7- use turbo_rcstr:: rcstr;
7+ use turbo_rcstr:: { RcStr , rcstr} ;
88use turbo_tasks:: { ResolvedVc , TryJoinIterExt , ValueToString , Vc , turbobail} ;
99use turbo_tasks_fs:: { File , FileContent , FileSystemPath } ;
1010use turbopack_core:: {
@@ -70,31 +70,14 @@ impl EcmascriptBrowserEvaluateChunk {
7070 ) )
7171 }
7272
73+ /// The params for bootstrapping: `{ otherChunks, runtimeModuleIds }`. This
74+ /// describes which other chunks must load and which runtime modules to instantiate.
75+ ///
76+ /// The emitted evaluate-chunk file ([`Self::code`]) reuses these same params,
77+ /// wrapping them as `push([selfPath, params])`. Next.js inlines them in production.
7378 #[ turbo_tasks:: function]
74- async fn code ( self : Vc < Self > ) -> Result < Vc < Code > > {
79+ pub ( crate ) async fn chunk_group_bootstrap_params ( self : Vc < Self > ) -> Result < Vc < RcStr > > {
7580 let this = self . await ?;
76- let source_maps = * this
77- . chunking_context
78- . reference_chunk_source_maps ( Vc :: upcast ( self ) )
79- . await ?;
80- // Lifetime hack to pull out the var into this scope
81- let chunk_path;
82- let script_or_path = match * this. chunking_context . current_chunk_method ( ) . await ? {
83- CurrentChunkMethod :: StringLiteral => {
84- let output_root = this. chunking_context . output_root ( ) . await ?;
85- let chunk_path_vc = self . path ( ) ;
86- chunk_path = chunk_path_vc. await ?;
87- let chunk_server_path = if let Some ( path) = output_root. get_path_to ( & chunk_path) {
88- path
89- } else {
90- turbobail ! ( "chunk path {chunk_path} is not in output root {output_root}" ) ;
91- } ;
92- Either :: Left ( StringifyJs ( chunk_server_path) )
93- }
94- CurrentChunkMethod :: DocumentCurrentScript => {
95- Either :: Right ( CURRENT_CHUNK_METHOD_DOCUMENT_CURRENT_SCRIPT_EXPR )
96- }
97- } ;
9881
9982 let other_chunks_data = self . chunks_data ( ) . await ?;
10083 let other_chunks_data = other_chunks_data. iter ( ) . try_join ( ) . await ?;
@@ -134,28 +117,56 @@ impl EcmascriptBrowserEvaluateChunk {
134117 runtime_module_ids,
135118 } ;
136119
137- let mut code = CodeBuilder :: new (
138- source_maps,
139- * this. chunking_context . debug_ids_enabled ( ) . await ?,
140- ) ;
120+ Ok ( Vc :: cell ( format ! ( "{}" , StringifyJs ( & params) ) . into ( ) ) )
121+ }
122+
123+ #[ turbo_tasks:: function]
124+ async fn code ( self : Vc < Self > ) -> Result < Vc < Code > > {
125+ let this = self . await ?;
126+ let source_maps = * this
127+ . chunking_context
128+ . reference_chunk_source_maps ( Vc :: upcast ( self ) )
129+ . await ?;
141130
142- // Use the configured chunk loading global variable to store the chunk here.
143- // This allows multiple runtimes to coexist on the same page when using different global
144- // names.
131+ let params = self . chunk_group_bootstrap_params ( ) . await ?;
145132 let chunk_loading_global = this. chunking_context . chunk_loading_global ( ) . await ?;
146- writedoc ! (
147- code,
148- // `||=` would be better but we need to be es2020 compatible
149- //`x || (x = default)` is better than `x = x || default` simply because we avoid _writing_ the property in the common case.
133+ // `||=` would be better but we need to be es2020 compatible.
134+ // `x || (x = default)` avoids _writing_ the property in the common case.
135+ let use_string_literal = matches ! (
136+ * this. chunking_context. current_chunk_method( ) . await ?,
137+ CurrentChunkMethod :: StringLiteral
138+ ) ;
139+ // Lifetime hack to keep the path read alive for the borrow below.
140+ let chunk_path;
141+ let script_or_path = if use_string_literal {
142+ let output_root = this. chunking_context . output_root ( ) . await ?;
143+ chunk_path = self . path ( ) . await ?;
144+ let chunk_server_path = if let Some ( path) = output_root. get_path_to ( & chunk_path) {
145+ path
146+ } else {
147+ turbobail ! ( "chunk path {chunk_path} is not in output root {output_root}" ) ;
148+ } ;
149+ Either :: Left ( StringifyJs ( chunk_server_path) )
150+ } else {
151+ Either :: Right ( CURRENT_CHUNK_METHOD_DOCUMENT_CURRENT_SCRIPT_EXPR )
152+ } ;
153+ let statement = formatdoc ! {
150154 r#"
151155 (globalThis[{chunk_loading_global}] || (globalThis[{chunk_loading_global}] = [])).push([
152156 {script_or_path},
153157 {params}
154158 ]);
155159 "# ,
156160 chunk_loading_global = StringifyJs ( & chunk_loading_global) ,
157- params = StringifyJs ( & params) ,
158- ) ?;
161+ script_or_path = script_or_path,
162+ params = & * * params,
163+ } ;
164+
165+ let mut code = CodeBuilder :: new (
166+ source_maps,
167+ * this. chunking_context . debug_ids_enabled ( ) . await ?,
168+ ) ;
169+ write ! ( code, "{}" , statement) ?;
159170
160171 let mut code = code. build ( ) ;
161172
0 commit comments