1use anyhow::Result;
2use bincode::{Decode, Encode};
3use turbo_rcstr::{RcStr, rcstr};
4use turbo_tasks::{ResolvedVc, Vc, trace::TraceRawVcs};
5use turbo_tasks_fs::FileSystemPath;
6use turbopack_browser::BrowserChunkingContext;
7use turbopack_core::{
8 chunk::{
9 AssetSuffix, ChunkingConfig, ChunkingContext, CrossOrigin, MangleType, MinifyType,
10 SourceMapSourceType, SourceMapsType, UnusedReferences, UrlBehavior,
11 chunk_id_strategy::ModuleIdStrategy,
12 },
13 compile_time_info::{CompileTimeDefines, CompileTimeInfo, FreeVarReference, FreeVarReferences},
14 environment::{EdgeWorkerEnvironment, Environment, ExecutionEnvironment, NodeJsVersion},
15 free_var_references,
16 issue::IssueSeverity,
17 module_graph::{
18 binding_usage_info::OptionBindingUsageInfo, style_groups::StyleGroupsAlgorithm,
19 },
20};
21use turbopack_css::chunk::CssChunkType;
22use turbopack_ecmascript::chunk::EcmascriptChunkType;
23use turbopack_node::execution_context::ExecutionContext;
24use turbopack_resolve::resolve_options_context::{ResolveOptionsContext, TsConfigHandling};
25
26use crate::{
27 app_structure::CollectedRootParams,
28 mode::NextMode,
29 next_config::NextConfig,
30 next_import_map::{get_next_edge_and_server_fallback_import_map, get_next_edge_import_map},
31 next_server::context::ServerContextType,
32 next_shared::resolve::NextSharedRuntimeResolvePlugin,
33 util::{
34 NextRuntime, OptionEnvMap, defines, foreign_code_context_condition,
35 free_var_references_with_vercel_system_env_warnings, worker_forwarded_globals,
36 },
37};
38
39#[turbo_tasks::function]
40async fn next_edge_defines(define_env: Vc<OptionEnvMap>) -> Result<Vc<CompileTimeDefines>> {
41 Ok(defines(&*define_env.await?).cell())
42}
43
44#[turbo_tasks::function]
47async fn next_edge_free_vars(
48 project_path: FileSystemPath,
49 define_env: Vc<OptionEnvMap>,
50 report_system_env_inlining: Vc<IssueSeverity>,
51) -> Result<Vc<FreeVarReferences>> {
52 Ok(free_var_references!(
53 ..free_var_references_with_vercel_system_env_warnings(
54 defines(&*define_env.await?),
55 *report_system_env_inlining.await?
56 ),
57 Buffer = FreeVarReference::EcmaScriptModule {
58 request: rcstr!("buffer"),
59 lookup_path: Some(project_path),
60 export: Some(rcstr!("Buffer")),
61 },
62 )
63 .cell())
64}
65
66#[turbo_tasks::function]
67pub async fn get_edge_compile_time_info(
68 project_path: FileSystemPath,
69 define_env: Vc<OptionEnvMap>,
70 node_version: ResolvedVc<NodeJsVersion>,
71 report_system_env_inlining: Vc<IssueSeverity>,
72 import_meta_env_base_url: RcStr,
73) -> Result<Vc<CompileTimeInfo>> {
74 CompileTimeInfo::builder(
75 Environment::new(ExecutionEnvironment::EdgeWorker(
76 EdgeWorkerEnvironment { node_version }.resolved_cell(),
77 ))
78 .to_resolved()
79 .await?,
80 )
81 .defines(next_edge_defines(define_env).to_resolved().await?)
82 .free_var_references(
83 next_edge_free_vars(project_path, define_env, report_system_env_inlining)
84 .to_resolved()
85 .await?,
86 )
87 .import_meta_env_base_url(import_meta_env_base_url)
88 .cell()
89 .await
90}
91
92#[turbo_tasks::function]
93pub async fn get_edge_resolve_options_context(
94 project_path: FileSystemPath,
95 ty: ServerContextType,
96 mode: Vc<NextMode>,
97 next_config: Vc<NextConfig>,
98 execution_context: Vc<ExecutionContext>,
99 collected_root_params: Option<Vc<CollectedRootParams>>,
100) -> Result<Vc<ResolveOptionsContext>> {
101 let next_edge_import_map = get_next_edge_import_map(
102 project_path.clone(),
103 ty.clone(),
104 next_config,
105 mode,
106 execution_context,
107 collected_root_params,
108 )
109 .to_resolved()
110 .await?;
111 let next_edge_fallback_import_map =
112 get_next_edge_and_server_fallback_import_map(project_path.clone(), NextRuntime::Edge)
113 .to_resolved()
114 .await?;
115
116 let after_resolve_plugins = vec![ResolvedVc::upcast(
117 NextSharedRuntimeResolvePlugin::new(project_path.clone())
118 .to_resolved()
119 .await?,
120 )];
121
122 let mut custom_conditions: Vec<_> = mode.await?.custom_resolve_conditions().collect();
124 custom_conditions.extend(NextRuntime::Edge.custom_resolve_conditions());
125
126 if ty.should_use_react_server_condition() {
127 custom_conditions.push(rcstr!("react-server"));
128 };
129
130 let resolve_options_context = ResolveOptionsContext {
135 enable_node_modules: Some(project_path.root().owned().await?),
136 enable_edge_node_externals: true,
137 custom_conditions,
138 import_map: Some(next_edge_import_map),
139 fallback_import_map: Some(next_edge_fallback_import_map),
140 module: true,
141 browser: true,
142 server_relative_root: Some(project_path.clone()),
145 after_resolve_plugins,
146
147 ..Default::default()
148 };
149
150 let tsconfig_path = next_config.typescript_tsconfig_path().await?;
151 let tsconfig_path = project_path.join(
152 tsconfig_path
153 .as_ref()
154 .unwrap_or(&rcstr!("tsconfig.json")),
157 )?;
158
159 Ok(ResolveOptionsContext {
160 enable_typescript: true,
161 enable_react: true,
162 enable_mjs_extension: true,
163 enable_edge_node_externals: true,
164 custom_extensions: next_config.resolve_extension().owned().await?,
165 tsconfig_path: TsConfigHandling::Fixed(tsconfig_path),
166 rules: vec![(
167 foreign_code_context_condition(next_config, project_path).await?,
168 resolve_options_context.clone().resolved_cell(),
169 )],
170 ..resolve_options_context
171 }
172 .cell())
173}
174
175#[turbo_tasks::task_input(contains_unresolved_vcs)]
176#[derive(Clone, Debug, PartialEq, Eq, Hash, TraceRawVcs, Encode, Decode)]
177pub struct EdgeChunkingContextOptions {
178 pub mode: Vc<NextMode>,
179 pub root_path: FileSystemPath,
180 pub node_root: FileSystemPath,
181 pub output_root_to_root_path: Vc<RcStr>,
182 pub environment: Vc<Environment>,
183 pub module_id_strategy: Vc<ModuleIdStrategy>,
184 pub export_usage: Vc<OptionBindingUsageInfo>,
185 pub unused_references: Vc<UnusedReferences>,
186 pub turbo_minify: Vc<bool>,
187 pub turbo_source_maps: Vc<SourceMapsType>,
188 pub no_mangling: Vc<bool>,
189 pub scope_hoisting: Vc<bool>,
190 pub nested_async_chunking: Vc<bool>,
191 pub client_root: FileSystemPath,
192 pub client_static_folder_name: RcStr,
193 pub asset_prefix: RcStr,
194 pub css_url_suffix: Vc<Option<RcStr>>,
195 pub hash_salt: ResolvedVc<RcStr>,
196 pub cross_origin: Vc<CrossOrigin>,
197 pub style_groups_algorithm: StyleGroupsAlgorithm,
198}
199
200#[turbo_tasks::function]
202pub async fn get_edge_chunking_context_with_client_assets(
203 options: EdgeChunkingContextOptions,
204) -> Result<Vc<Box<dyn ChunkingContext>>> {
205 let EdgeChunkingContextOptions {
206 mode,
207 root_path,
208 node_root,
209 output_root_to_root_path,
210 environment,
211 module_id_strategy,
212 export_usage,
213 unused_references,
214 turbo_minify,
215 turbo_source_maps,
216 no_mangling,
217 scope_hoisting,
218 nested_async_chunking,
219 client_root,
220 client_static_folder_name,
221 asset_prefix,
222 css_url_suffix,
223 hash_salt,
224 cross_origin,
225 style_groups_algorithm,
226 } = options;
227 let cross_origin_loading = *cross_origin.await?;
228 let output_root = node_root.join("server/edge")?;
229 let next_mode = mode.await?;
230 let mut builder = BrowserChunkingContext::builder(
231 root_path,
232 output_root.clone(),
233 output_root_to_root_path.owned().await?,
234 client_root.clone(),
235 output_root.join("chunks/ssr")?,
236 client_root
237 .join(&client_static_folder_name)?
238 .join("media")?,
239 environment.to_resolved().await?,
240 next_mode.runtime_type(),
241 )
242 .asset_base_path(Some(asset_prefix))
243 .default_url_behavior(UrlBehavior {
244 suffix: AssetSuffix::FromGlobal(rcstr!("NEXT_CLIENT_ASSET_SUFFIX")),
245 static_suffix: css_url_suffix.to_resolved().await?,
246 })
247 .minify_type(if *turbo_minify.await? {
248 MinifyType::Minify {
249 mangle: (!*no_mangling.await?).then_some(MangleType::Deterministic),
251 }
252 } else {
253 MinifyType::NoMinify
254 })
255 .source_maps(*turbo_source_maps.await?)
256 .source_map_source_type(if next_mode.is_development() {
261 SourceMapSourceType::AbsoluteFileUri
262 } else {
263 SourceMapSourceType::RelativeUri
264 })
265 .cross_origin(cross_origin_loading)
266 .module_id_strategy(module_id_strategy.to_resolved().await?)
267 .export_usage(*export_usage.await?)
268 .unused_references(unused_references.to_resolved().await?)
269 .hash_salt(hash_salt)
270 .nested_async_availability(*nested_async_chunking.await?)
271 .worker_forwarded_globals(worker_forwarded_globals());
272
273 if !next_mode.is_development() {
274 builder = builder
275 .chunking_config(
276 Vc::<EcmascriptChunkType>::default().to_resolved().await?,
277 ChunkingConfig {
278 min_chunk_size: 20_000,
279 ..Default::default()
280 },
281 )
282 .chunking_config(
283 Vc::<CssChunkType>::default().to_resolved().await?,
284 ChunkingConfig {
285 max_merge_chunk_size: 100_000,
286 style_groups_algorithm: style_groups_algorithm.clone(),
287 ..Default::default()
288 },
289 )
290 .module_merging(*scope_hoisting.await?);
291 }
292
293 Ok(Vc::upcast(builder.build()))
294}
295
296#[turbo_tasks::function]
298pub async fn get_edge_chunking_context(
299 options: EdgeChunkingContextOptions,
300) -> Result<Vc<Box<dyn ChunkingContext>>> {
301 let EdgeChunkingContextOptions {
302 mode,
303 root_path,
304 node_root,
305 output_root_to_root_path,
306 environment,
307 module_id_strategy,
308 export_usage,
309 unused_references,
310 turbo_minify,
311 turbo_source_maps,
312 no_mangling,
313 scope_hoisting,
314 nested_async_chunking,
315 client_root,
316 client_static_folder_name,
317 asset_prefix,
318 css_url_suffix,
319 hash_salt,
320 cross_origin,
321 style_groups_algorithm,
322 } = options;
323 let cross_origin = *cross_origin.await?;
324 let css_url_suffix = css_url_suffix.to_resolved().await?;
325 let output_root = node_root.join("server/edge")?;
326 let next_mode = mode.await?;
327 let mut builder = BrowserChunkingContext::builder(
328 root_path,
329 output_root.clone(),
330 output_root_to_root_path.owned().await?,
331 output_root.clone(),
332 output_root.join("chunks")?,
333 output_root.join("assets")?,
334 environment.to_resolved().await?,
335 next_mode.runtime_type(),
336 )
337 .client_roots_override(rcstr!("client"), client_root.clone())
338 .asset_root_path_override(
339 rcstr!("client"),
340 client_root
341 .join(&client_static_folder_name)?
342 .join("media")?,
343 )
344 .asset_base_path_override(rcstr!("client"), asset_prefix)
345 .url_behavior_override(
346 rcstr!("client"),
347 UrlBehavior {
348 suffix: AssetSuffix::FromGlobal(rcstr!("NEXT_CLIENT_ASSET_SUFFIX")),
349 static_suffix: css_url_suffix,
350 },
351 )
352 .default_url_behavior(UrlBehavior {
353 suffix: AssetSuffix::Inferred,
354 static_suffix: ResolvedVc::cell(None),
355 })
356 .asset_base_path(Some(rcstr!("blob:server/edge/")))
361 .minify_type(if *turbo_minify.await? {
362 MinifyType::Minify {
363 mangle: (!*no_mangling.await?).then_some(MangleType::OptimalSize),
364 }
365 } else {
366 MinifyType::NoMinify
367 })
368 .source_maps(*turbo_source_maps.await?)
369 .source_map_source_type(if next_mode.is_development() {
374 SourceMapSourceType::AbsoluteFileUri
375 } else {
376 SourceMapSourceType::RelativeUri
377 })
378 .cross_origin(cross_origin)
379 .module_id_strategy(module_id_strategy.to_resolved().await?)
380 .export_usage(*export_usage.await?)
381 .unused_references(unused_references.to_resolved().await?)
382 .hash_salt(hash_salt)
383 .nested_async_availability(*nested_async_chunking.await?)
384 .worker_forwarded_globals(worker_forwarded_globals());
385
386 if !next_mode.is_development() {
387 builder = builder
388 .chunking_config(
389 Vc::<EcmascriptChunkType>::default().to_resolved().await?,
390 ChunkingConfig {
391 min_chunk_size: 20_000,
392 ..Default::default()
393 },
394 )
395 .chunking_config(
396 Vc::<CssChunkType>::default().to_resolved().await?,
397 ChunkingConfig {
398 max_merge_chunk_size: 100_000,
399 style_groups_algorithm: style_groups_algorithm.clone(),
400 ..Default::default()
401 },
402 )
403 .module_merging(*scope_hoisting.await?);
404 }
405
406 Ok(Vc::upcast(builder.build()))
407}