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