1use anyhow::Result;
2use bincode::{Decode, Encode};
3use turbo_rcstr::{RcStr, rcstr};
4use turbo_tasks::{ResolvedVc, TaskInput, Vc, trace::TraceRawVcs};
5use turbo_tasks_fs::FileSystemPath;
6use turbopack_browser::BrowserChunkingContext;
7use turbopack_core::{
8 chunk::{
9 ChunkingConfig, ChunkingContext, MangleType, MinifyType, SourceMapsType,
10 module_id_strategies::ModuleIdStrategy,
11 },
12 compile_time_info::{CompileTimeDefines, CompileTimeInfo, FreeVarReference, FreeVarReferences},
13 environment::{EdgeWorkerEnvironment, Environment, ExecutionEnvironment, NodeJsVersion},
14 free_var_references,
15 module_graph::binding_usage_info::OptionBindingUsageInfo,
16};
17use turbopack_css::chunk::CssChunkType;
18use turbopack_ecmascript::chunk::EcmascriptChunkType;
19use turbopack_node::execution_context::ExecutionContext;
20use turbopack_resolve::resolve_options_context::ResolveOptionsContext;
21
22use crate::{
23 app_structure::CollectedRootParams,
24 mode::NextMode,
25 next_config::NextConfig,
26 next_font::local::NextFontLocalResolvePlugin,
27 next_import_map::{get_next_edge_and_server_fallback_import_map, get_next_edge_import_map},
28 next_server::context::ServerContextType,
29 next_shared::resolve::{
30 ModuleFeatureReportResolvePlugin, NextSharedRuntimeResolvePlugin,
31 get_invalid_client_only_resolve_plugin, get_invalid_styled_jsx_resolve_plugin,
32 },
33 util::{NextRuntime, OptionEnvMap, defines, foreign_code_context_condition},
34};
35
36#[turbo_tasks::function]
37async fn next_edge_defines(define_env: Vc<OptionEnvMap>) -> Result<Vc<CompileTimeDefines>> {
38 Ok(defines(&*define_env.await?).cell())
39}
40
41#[turbo_tasks::function]
44async fn next_edge_free_vars(
45 project_path: FileSystemPath,
46 define_env: Vc<OptionEnvMap>,
47) -> Result<Vc<FreeVarReferences>> {
48 Ok(free_var_references!(
49 ..defines(&*define_env.await?).into_iter(),
50 Buffer = FreeVarReference::EcmaScriptModule {
51 request: rcstr!("buffer"),
52 lookup_path: Some(project_path),
53 export: Some(rcstr!("Buffer")),
54 },
55 )
56 .cell())
57}
58
59#[turbo_tasks::function]
60pub async fn get_edge_compile_time_info(
61 project_path: FileSystemPath,
62 define_env: Vc<OptionEnvMap>,
63 node_version: ResolvedVc<NodeJsVersion>,
64) -> Result<Vc<CompileTimeInfo>> {
65 CompileTimeInfo::builder(
66 Environment::new(ExecutionEnvironment::EdgeWorker(
67 EdgeWorkerEnvironment { node_version }.resolved_cell(),
68 ))
69 .to_resolved()
70 .await?,
71 )
72 .defines(next_edge_defines(define_env).to_resolved().await?)
73 .free_var_references(
74 next_edge_free_vars(project_path, define_env)
75 .to_resolved()
76 .await?,
77 )
78 .cell()
79 .await
80}
81
82#[turbo_tasks::function]
83pub async fn get_edge_resolve_options_context(
84 project_path: FileSystemPath,
85 ty: ServerContextType,
86 mode: Vc<NextMode>,
87 next_config: Vc<NextConfig>,
88 execution_context: Vc<ExecutionContext>,
89 collected_root_params: Option<Vc<CollectedRootParams>>,
90) -> Result<Vc<ResolveOptionsContext>> {
91 let next_edge_import_map = get_next_edge_import_map(
92 project_path.clone(),
93 ty.clone(),
94 next_config,
95 mode,
96 execution_context,
97 collected_root_params,
98 )
99 .to_resolved()
100 .await?;
101 let next_edge_fallback_import_map =
102 get_next_edge_and_server_fallback_import_map(project_path.clone(), NextRuntime::Edge)
103 .to_resolved()
104 .await?;
105
106 let mut before_resolve_plugins = vec![ResolvedVc::upcast(
107 ModuleFeatureReportResolvePlugin::new(project_path.clone())
108 .to_resolved()
109 .await?,
110 )];
111 if matches!(
112 ty,
113 ServerContextType::Pages { .. }
114 | ServerContextType::AppSSR { .. }
115 | ServerContextType::AppRSC { .. }
116 ) {
117 before_resolve_plugins.push(ResolvedVc::upcast(
118 NextFontLocalResolvePlugin::new(project_path.clone())
119 .to_resolved()
120 .await?,
121 ));
122 };
123
124 if matches!(
125 ty,
126 ServerContextType::AppRSC { .. }
127 | ServerContextType::AppRoute { .. }
128 | ServerContextType::Middleware { .. }
129 | ServerContextType::Instrumentation { .. }
130 ) {
131 before_resolve_plugins.push(ResolvedVc::upcast(
132 get_invalid_client_only_resolve_plugin(project_path.clone())
133 .to_resolved()
134 .await?,
135 ));
136 before_resolve_plugins.push(ResolvedVc::upcast(
137 get_invalid_styled_jsx_resolve_plugin(project_path.clone())
138 .to_resolved()
139 .await?,
140 ));
141 }
142
143 let after_resolve_plugins = vec![ResolvedVc::upcast(
144 NextSharedRuntimeResolvePlugin::new(project_path.clone())
145 .to_resolved()
146 .await?,
147 )];
148
149 let mut custom_conditions: Vec<_> = mode.await?.custom_resolve_conditions().collect();
151 custom_conditions.extend(NextRuntime::Edge.custom_resolve_conditions());
152
153 if ty.should_use_react_server_condition() {
154 custom_conditions.push(rcstr!("react-server"));
155 };
156
157 let resolve_options_context = ResolveOptionsContext {
162 enable_node_modules: Some(project_path.root().owned().await?),
163 enable_edge_node_externals: true,
164 custom_conditions,
165 import_map: Some(next_edge_import_map),
166 fallback_import_map: Some(next_edge_fallback_import_map),
167 module: true,
168 browser: true,
169 after_resolve_plugins,
170 before_resolve_plugins,
171
172 ..Default::default()
173 };
174
175 Ok(ResolveOptionsContext {
176 enable_typescript: true,
177 enable_react: true,
178 enable_mjs_extension: true,
179 enable_edge_node_externals: true,
180 custom_extensions: next_config.resolve_extension().owned().await?,
181 tsconfig_path: next_config
182 .typescript_tsconfig_path()
183 .await?
184 .as_ref()
185 .or(Some(&RcStr::from("tsconfig.json")))
188 .map(|p| project_path.join(p))
189 .transpose()?,
190 rules: vec![(
191 foreign_code_context_condition(next_config, project_path).await?,
192 resolve_options_context.clone().resolved_cell(),
193 )],
194 ..resolve_options_context
195 }
196 .cell())
197}
198
199#[derive(Clone, Debug, PartialEq, Eq, Hash, TaskInput, TraceRawVcs, Encode, Decode)]
200pub struct EdgeChunkingContextOptions {
201 pub mode: Vc<NextMode>,
202 pub root_path: FileSystemPath,
203 pub node_root: FileSystemPath,
204 pub output_root_to_root_path: Vc<RcStr>,
205 pub environment: Vc<Environment>,
206 pub module_id_strategy: Vc<Box<dyn ModuleIdStrategy>>,
207 pub export_usage: Vc<OptionBindingUsageInfo>,
208 pub unused_references: Vc<OptionBindingUsageInfo>,
209 pub turbo_minify: Vc<bool>,
210 pub turbo_source_maps: Vc<SourceMapsType>,
211 pub no_mangling: Vc<bool>,
212 pub scope_hoisting: Vc<bool>,
213 pub nested_async_chunking: Vc<bool>,
214 pub client_root: FileSystemPath,
215 pub asset_prefix: RcStr,
216}
217
218#[turbo_tasks::function]
220pub async fn get_edge_chunking_context_with_client_assets(
221 options: EdgeChunkingContextOptions,
222) -> Result<Vc<Box<dyn ChunkingContext>>> {
223 let EdgeChunkingContextOptions {
224 mode,
225 root_path,
226 node_root,
227 output_root_to_root_path,
228 environment,
229 module_id_strategy,
230 export_usage,
231 unused_references,
232 turbo_minify,
233 turbo_source_maps,
234 no_mangling,
235 scope_hoisting,
236 nested_async_chunking,
237 client_root,
238 asset_prefix,
239 } = options;
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.join("static/media")?,
249 environment.to_resolved().await?,
250 next_mode.runtime_type(),
251 )
252 .asset_base_path(Some(asset_prefix))
253 .minify_type(if *turbo_minify.await? {
254 MinifyType::Minify {
255 mangle: (!*no_mangling.await?).then_some(MangleType::Deterministic),
257 }
258 } else {
259 MinifyType::NoMinify
260 })
261 .source_maps(*turbo_source_maps.await?)
262 .module_id_strategy(module_id_strategy.to_resolved().await?)
263 .export_usage(*export_usage.await?)
264 .unused_references(*unused_references.await?)
265 .nested_async_availability(*nested_async_chunking.await?);
266
267 if !next_mode.is_development() {
268 builder = builder
269 .chunking_config(
270 Vc::<EcmascriptChunkType>::default().to_resolved().await?,
271 ChunkingConfig {
272 min_chunk_size: 20_000,
273 ..Default::default()
274 },
275 )
276 .chunking_config(
277 Vc::<CssChunkType>::default().to_resolved().await?,
278 ChunkingConfig {
279 max_merge_chunk_size: 100_000,
280 ..Default::default()
281 },
282 )
283 .module_merging(*scope_hoisting.await?);
284 }
285
286 Ok(Vc::upcast(builder.build()))
287}
288
289#[turbo_tasks::function]
291pub async fn get_edge_chunking_context(
292 options: EdgeChunkingContextOptions,
293) -> Result<Vc<Box<dyn ChunkingContext>>> {
294 let EdgeChunkingContextOptions {
295 mode,
296 root_path,
297 node_root,
298 output_root_to_root_path,
299 environment,
300 module_id_strategy,
301 export_usage,
302 unused_references,
303 turbo_minify,
304 turbo_source_maps,
305 no_mangling,
306 scope_hoisting,
307 nested_async_chunking,
308 client_root,
309 asset_prefix,
310 } = options;
311 let output_root = node_root.join("server/edge")?;
312 let next_mode = mode.await?;
313 let mut builder = BrowserChunkingContext::builder(
314 root_path,
315 output_root.clone(),
316 output_root_to_root_path.owned().await?,
317 output_root.clone(),
318 output_root.join("chunks")?,
319 output_root.join("assets")?,
320 environment.to_resolved().await?,
321 next_mode.runtime_type(),
322 )
323 .client_roots_override(rcstr!("client"), client_root.clone())
324 .asset_root_path_override(rcstr!("client"), client_root.join("static/media")?)
325 .asset_base_path_override(rcstr!("client"), asset_prefix)
326 .asset_base_path(Some(rcstr!("blob:server/edge/")))
331 .minify_type(if *turbo_minify.await? {
332 MinifyType::Minify {
333 mangle: (!*no_mangling.await?).then_some(MangleType::OptimalSize),
334 }
335 } else {
336 MinifyType::NoMinify
337 })
338 .source_maps(*turbo_source_maps.await?)
339 .module_id_strategy(module_id_strategy.to_resolved().await?)
340 .export_usage(*export_usage.await?)
341 .unused_references(*unused_references.await?)
342 .nested_async_availability(*nested_async_chunking.await?);
343
344 if !next_mode.is_development() {
345 builder = builder
346 .chunking_config(
347 Vc::<EcmascriptChunkType>::default().to_resolved().await?,
348 ChunkingConfig {
349 min_chunk_size: 20_000,
350 ..Default::default()
351 },
352 )
353 .chunking_config(
354 Vc::<CssChunkType>::default().to_resolved().await?,
355 ChunkingConfig {
356 max_merge_chunk_size: 100_000,
357 ..Default::default()
358 },
359 )
360 .module_merging(*scope_hoisting.await?);
361 }
362
363 Ok(Vc::upcast(builder.build()))
364}