1use anyhow::Result;
2use bincode::{Decode, Encode};
3use turbo_rcstr::{RcStr, rcstr};
4use turbo_tasks::{ResolvedVc, Vc};
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().to_resolved().await?,
118 )];
119
120 let mut custom_conditions: Vec<_> = mode.await?.custom_resolve_conditions().collect();
122 custom_conditions.extend(NextRuntime::Edge.custom_resolve_conditions());
123
124 if ty.should_use_react_server_condition() {
125 custom_conditions.push(rcstr!("react-server"));
126 };
127
128 let resolve_options_context = ResolveOptionsContext {
133 enable_node_modules: Some(project_path.root().owned().await?),
134 enable_edge_node_externals: true,
135 custom_conditions,
136 import_map: Some(next_edge_import_map),
137 fallback_import_map: Some(next_edge_fallback_import_map),
138 module: true,
139 browser: true,
140 server_relative_root: Some(project_path.clone()),
143 after_resolve_plugins,
144
145 ..Default::default()
146 };
147
148 let tsconfig_path = next_config.typescript_tsconfig_path().await?;
149 let tsconfig_path = project_path.join(
150 tsconfig_path
151 .as_ref()
152 .unwrap_or(&rcstr!("tsconfig.json")),
155 )?;
156
157 Ok(ResolveOptionsContext {
158 enable_typescript: true,
159 enable_react: true,
160 enable_mjs_extension: true,
161 enable_edge_node_externals: true,
162 custom_extensions: next_config.resolve_extension().owned().await?,
163 tsconfig_path: TsConfigHandling::Fixed(tsconfig_path),
164 rules: vec![(
165 foreign_code_context_condition(next_config, project_path).await?,
166 resolve_options_context.clone().resolved_cell(),
167 )],
168 ..resolve_options_context
169 }
170 .cell())
171}
172
173#[turbo_tasks::task_input(contains_unresolved_vcs)]
174#[derive(Clone, Debug, PartialEq, Eq, Hash, Encode, Decode)]
175pub struct EdgeChunkingContextOptions {
176 pub mode: Vc<NextMode>,
177 pub root_path: FileSystemPath,
178 pub node_root: FileSystemPath,
179 pub output_root_to_root_path: Vc<RcStr>,
180 pub environment: Vc<Environment>,
181 pub module_id_strategy: Vc<ModuleIdStrategy>,
182 pub export_usage: Vc<OptionBindingUsageInfo>,
183 pub unused_references: Vc<UnusedReferences>,
184 pub turbo_minify: Vc<bool>,
185 pub turbo_source_maps: Vc<SourceMapsType>,
186 pub no_mangling: Vc<bool>,
187 pub scope_hoisting: Vc<bool>,
188 pub nested_async_chunking: Vc<bool>,
189 pub client_root: FileSystemPath,
190 pub client_static_folder_name: RcStr,
191 pub asset_prefix: RcStr,
192 pub css_url_suffix: Vc<Option<RcStr>>,
193 pub hash_salt: ResolvedVc<RcStr>,
194 pub cross_origin: Vc<CrossOrigin>,
195 pub style_groups_algorithm: StyleGroupsAlgorithm,
196}
197
198#[turbo_tasks::function]
200pub async fn get_edge_chunking_context_with_client_assets(
201 options: EdgeChunkingContextOptions,
202) -> Result<Vc<Box<dyn ChunkingContext>>> {
203 let EdgeChunkingContextOptions {
204 mode,
205 root_path,
206 node_root,
207 output_root_to_root_path,
208 environment,
209 module_id_strategy,
210 export_usage,
211 unused_references,
212 turbo_minify,
213 turbo_source_maps,
214 no_mangling,
215 scope_hoisting,
216 nested_async_chunking,
217 client_root,
218 client_static_folder_name,
219 asset_prefix,
220 css_url_suffix,
221 hash_salt,
222 cross_origin,
223 style_groups_algorithm,
224 } = options;
225 let cross_origin_loading = *cross_origin.await?;
226 let output_root = node_root.join("server/edge")?;
227 let next_mode = mode.await?;
228 let mut builder = BrowserChunkingContext::builder(
229 root_path,
230 output_root.clone(),
231 output_root_to_root_path.owned().await?,
232 client_root.clone(),
233 output_root.join("chunks/ssr")?,
234 client_root
235 .join(&client_static_folder_name)?
236 .join("media")?,
237 environment.to_resolved().await?,
238 next_mode.runtime_type(),
239 )
240 .asset_base_path(Some(asset_prefix))
241 .default_url_behavior(UrlBehavior {
242 suffix: AssetSuffix::FromGlobal(rcstr!("NEXT_CLIENT_ASSET_SUFFIX")),
243 static_suffix: css_url_suffix.to_resolved().await?,
244 })
245 .minify_type(if *turbo_minify.await? {
246 MinifyType::Minify {
247 mangle: (!*no_mangling.await?).then_some(MangleType::Deterministic),
249 }
250 } else {
251 MinifyType::NoMinify
252 })
253 .source_maps(*turbo_source_maps.await?)
254 .source_map_source_type(if next_mode.is_development() {
259 SourceMapSourceType::AbsoluteFileUri
260 } else {
261 SourceMapSourceType::RelativeUri
262 })
263 .cross_origin(cross_origin_loading)
264 .module_id_strategy(module_id_strategy.to_resolved().await?)
265 .export_usage(*export_usage.await?)
266 .unused_references(unused_references.to_resolved().await?)
267 .hash_salt(hash_salt)
268 .nested_async_availability(*nested_async_chunking.await?)
269 .worker_forwarded_globals(worker_forwarded_globals());
270
271 if !next_mode.is_development() {
272 builder = builder
273 .chunking_config(
274 Vc::<EcmascriptChunkType>::default().to_resolved().await?,
275 ChunkingConfig {
276 min_chunk_size: 20_000,
277 ..Default::default()
278 },
279 )
280 .chunking_config(
281 Vc::<CssChunkType>::default().to_resolved().await?,
282 ChunkingConfig {
283 max_merge_chunk_size: 100_000,
284 style_groups_algorithm: style_groups_algorithm.clone(),
285 ..Default::default()
286 },
287 )
288 .module_merging(*scope_hoisting.await?);
289 }
290
291 Ok(Vc::upcast(builder.build()))
292}
293
294#[turbo_tasks::function]
296pub async fn get_edge_chunking_context(
297 options: EdgeChunkingContextOptions,
298) -> Result<Vc<Box<dyn ChunkingContext>>> {
299 let EdgeChunkingContextOptions {
300 mode,
301 root_path,
302 node_root,
303 output_root_to_root_path,
304 environment,
305 module_id_strategy,
306 export_usage,
307 unused_references,
308 turbo_minify,
309 turbo_source_maps,
310 no_mangling,
311 scope_hoisting,
312 nested_async_chunking,
313 client_root,
314 client_static_folder_name,
315 asset_prefix,
316 css_url_suffix,
317 hash_salt,
318 cross_origin,
319 style_groups_algorithm,
320 } = options;
321 let cross_origin = *cross_origin.await?;
322 let css_url_suffix = css_url_suffix.to_resolved().await?;
323 let output_root = node_root.join("server/edge")?;
324 let next_mode = mode.await?;
325 let mut builder = BrowserChunkingContext::builder(
326 root_path,
327 output_root.clone(),
328 output_root_to_root_path.owned().await?,
329 output_root.clone(),
330 output_root.join("chunks")?,
331 output_root.join("assets")?,
332 environment.to_resolved().await?,
333 next_mode.runtime_type(),
334 )
335 .client_roots_override(rcstr!("client"), client_root.clone())
336 .asset_root_path_override(
337 rcstr!("client"),
338 client_root
339 .join(&client_static_folder_name)?
340 .join("media")?,
341 )
342 .asset_base_path_override(rcstr!("client"), asset_prefix)
343 .url_behavior_override(
344 rcstr!("client"),
345 UrlBehavior {
346 suffix: AssetSuffix::FromGlobal(rcstr!("NEXT_CLIENT_ASSET_SUFFIX")),
347 static_suffix: css_url_suffix,
348 },
349 )
350 .default_url_behavior(UrlBehavior {
351 suffix: AssetSuffix::Inferred,
352 static_suffix: ResolvedVc::cell(None),
353 })
354 .asset_base_path(Some(rcstr!("blob:server/edge/")))
359 .minify_type(if *turbo_minify.await? {
360 MinifyType::Minify {
361 mangle: (!*no_mangling.await?).then_some(MangleType::OptimalSize),
362 }
363 } else {
364 MinifyType::NoMinify
365 })
366 .source_maps(*turbo_source_maps.await?)
367 .source_map_source_type(if next_mode.is_development() {
372 SourceMapSourceType::AbsoluteFileUri
373 } else {
374 SourceMapSourceType::RelativeUri
375 })
376 .cross_origin(cross_origin)
377 .module_id_strategy(module_id_strategy.to_resolved().await?)
378 .export_usage(*export_usage.await?)
379 .unused_references(unused_references.to_resolved().await?)
380 .hash_salt(hash_salt)
381 .nested_async_availability(*nested_async_chunking.await?)
382 .worker_forwarded_globals(worker_forwarded_globals());
383
384 if !next_mode.is_development() {
385 builder = builder
386 .chunking_config(
387 Vc::<EcmascriptChunkType>::default().to_resolved().await?,
388 ChunkingConfig {
389 min_chunk_size: 20_000,
390 ..Default::default()
391 },
392 )
393 .chunking_config(
394 Vc::<CssChunkType>::default().to_resolved().await?,
395 ChunkingConfig {
396 max_merge_chunk_size: 100_000,
397 style_groups_algorithm: style_groups_algorithm.clone(),
398 ..Default::default()
399 },
400 )
401 .module_merging(*scope_hoisting.await?);
402 }
403
404 Ok(Vc::upcast(builder.build()))
405}