Skip to main content

next_core/next_client/
context.rs

1use std::collections::BTreeSet;
2
3use anyhow::Result;
4use bincode::{Decode, Encode};
5use turbo_rcstr::{RcStr, rcstr};
6use turbo_tasks::{ResolvedVc, Vc, trace::TraceRawVcs};
7use turbo_tasks_fs::FileSystemPath;
8use turbopack::module_options::{
9    CssOptionsContext, EcmascriptOptionsContext, JsxTransformOptions, TypescriptTransformOptions,
10    module_options_context::ModuleOptionsContext, side_effect_free_packages_glob,
11};
12use turbopack_browser::{
13    BrowserChunkingContext, CurrentChunkMethod, react_refresh::assert_can_resolve_react_refresh,
14};
15use turbopack_core::{
16    chunk::{
17        AssetSuffix, ChunkLoadRetry, ChunkingConfig, ChunkingContext, ContentHashing, CrossOrigin,
18        MangleType, MinifyType, SourceMapSourceType, SourceMapsType, UnusedReferences, UrlBehavior,
19        chunk_id_strategy::ModuleIdStrategy,
20    },
21    compile_time_info::{CompileTimeDefines, CompileTimeInfo, FreeVarReference, FreeVarReferences},
22    environment::{BrowserEnvironment, Environment, ExecutionEnvironment},
23    free_var_references,
24    issue::IssueSeverity,
25    module_graph::{
26        binding_usage_info::OptionBindingUsageInfo, style_groups::StyleGroupsAlgorithm,
27    },
28    resolve::{parse::Request, pattern::Pattern},
29};
30use turbopack_css::chunk::CssChunkType;
31use turbopack_ecmascript::{
32    AnalyzeMode, TypeofWindow,
33    chunk::EcmascriptChunkType,
34    references::esm::UrlRewriteBehavior,
35    transform::{PresetEnvConfig, ReactCompilerTarget},
36};
37use turbopack_node::{
38    execution_context::ExecutionContext,
39    transforms::postcss::{PostCssConfigLocation, PostCssTransformOptions},
40};
41use turbopack_resolve::resolve_options_context::{ResolveOptionsContext, TsConfigHandling};
42
43use crate::{
44    mode::NextMode,
45    next_build::get_postcss_package_mapping,
46    next_client::{
47        runtime_entry::{RuntimeEntries, RuntimeEntry},
48        transforms::get_next_client_transforms_rules,
49    },
50    next_config::NextConfig,
51    next_import_map::{
52        get_next_client_fallback_import_map, get_next_client_import_map,
53        get_next_client_resolved_map,
54    },
55    next_shared::{
56        resolve::NextSharedRuntimeResolvePlugin,
57        webpack_rules::{
58            WebpackLoaderBuiltinCondition, babel::detect_react_compiler_target,
59            webpack_loader_options,
60        },
61    },
62    transform_options::{
63        get_decorators_transform_options, get_jsx_transform_options,
64        get_typescript_transform_options,
65    },
66    util::{
67        OptionEnvMap, defines, foreign_code_context_condition,
68        free_var_references_with_vercel_system_env_warnings, internal_assets_conditions,
69        module_styles_rule_condition, worker_forwarded_globals,
70    },
71};
72
73#[turbo_tasks::function]
74async fn next_client_defines(define_env: Vc<OptionEnvMap>) -> Result<Vc<CompileTimeDefines>> {
75    Ok(defines(&*define_env.await?).cell())
76}
77
78#[turbo_tasks::function]
79async fn next_client_free_vars(
80    define_env: Vc<OptionEnvMap>,
81    report_system_env_inlining: Vc<IssueSeverity>,
82) -> Result<Vc<FreeVarReferences>> {
83    Ok(free_var_references!(
84        ..free_var_references_with_vercel_system_env_warnings(
85            defines(&*define_env.await?),
86            *report_system_env_inlining.await?
87        ),
88        Buffer = FreeVarReference::EcmaScriptModule {
89            request: rcstr!("node:buffer"),
90            lookup_path: None,
91            export: Some(rcstr!("Buffer")),
92        },
93        process = FreeVarReference::EcmaScriptModule {
94            request: rcstr!("node:process"),
95            lookup_path: None,
96            export: Some(rcstr!("default")),
97        }
98    )
99    .cell())
100}
101
102#[turbo_tasks::function]
103pub async fn get_client_compile_time_info(
104    browserslist_query: RcStr,
105    define_env: Vc<OptionEnvMap>,
106    report_system_env_inlining: Vc<IssueSeverity>,
107    hot_module_replacement_enabled: bool,
108    import_meta_env_base_url: RcStr,
109) -> Result<Vc<CompileTimeInfo>> {
110    CompileTimeInfo::builder(
111        Environment::new(ExecutionEnvironment::Browser(
112            BrowserEnvironment {
113                dom: true,
114                web_worker: false,
115                service_worker: false,
116                browserslist_query: browserslist_query.to_owned(),
117            }
118            .resolved_cell(),
119        ))
120        .to_resolved()
121        .await?,
122    )
123    .defines(next_client_defines(define_env).to_resolved().await?)
124    .free_var_references(
125        next_client_free_vars(define_env, report_system_env_inlining)
126            .to_resolved()
127            .await?,
128    )
129    .hot_module_replacement_enabled(hot_module_replacement_enabled)
130    .import_meta_env_base_url(import_meta_env_base_url)
131    .cell()
132    .await
133}
134
135#[turbo_tasks::value(shared, task_input)]
136#[derive(Debug, Clone, Hash)]
137pub enum ClientContextType {
138    Pages { pages_dir: FileSystemPath },
139    App { app_dir: FileSystemPath },
140    Fallback,
141    Other,
142}
143
144#[turbo_tasks::function]
145pub async fn get_client_resolve_options_context(
146    project_path: FileSystemPath,
147    ty: ClientContextType,
148    mode: Vc<NextMode>,
149    next_config: Vc<NextConfig>,
150    execution_context: Vc<ExecutionContext>,
151) -> Result<Vc<ResolveOptionsContext>> {
152    let next_client_import_map = get_next_client_import_map(
153        project_path.clone(),
154        ty.clone(),
155        next_config,
156        mode,
157        execution_context,
158    )
159    .to_resolved()
160    .await?;
161    let next_client_fallback_import_map = get_next_client_fallback_import_map(ty.clone())
162        .to_resolved()
163        .await?;
164    let expose_testing_api = mode.await?.is_development()
165        || *next_config
166            .enable_expose_testing_api_in_production_build()
167            .await?;
168    let concurrent_router_queue = *next_config.enable_concurrent_router_queue().await?;
169    let next_client_resolved_map = get_next_client_resolved_map(
170        project_path.clone(),
171        project_path.clone(),
172        *mode.await?,
173        expose_testing_api,
174        concurrent_router_queue,
175    )
176    .await?
177    .to_resolved()
178    .await?;
179    let mut custom_conditions: Vec<_> = mode.await?.custom_resolve_conditions().collect();
180
181    if *next_config.enable_cache_components().await? {
182        custom_conditions.push(rcstr!("next-js"));
183    };
184
185    let resolve_options_context = ResolveOptionsContext {
186        enable_node_modules: Some(project_path.root().owned().await?),
187        custom_conditions,
188        import_map: Some(next_client_import_map),
189        fallback_import_map: Some(next_client_fallback_import_map),
190        resolved_map: Some(next_client_resolved_map),
191        browser: true,
192        module: true,
193        // A request starting with `/` is resolved from the project directory, which is not
194        // necessarily the root of the filesystem (e.g. in a monorepo).
195        server_relative_root: Some(project_path.clone()),
196        after_resolve_plugins: vec![ResolvedVc::upcast(
197            NextSharedRuntimeResolvePlugin::new(project_path.clone())
198                .to_resolved()
199                .await?,
200        )],
201        ..Default::default()
202    };
203
204    let tsconfig_path = next_config.typescript_tsconfig_path().await?;
205    let tsconfig_path = project_path.join(
206        tsconfig_path
207            .as_ref()
208            // Fall back to tsconfig only for resolving. This is because we don't want Turbopack to
209            // resolve tsconfig.json relative to the file being compiled.
210            .unwrap_or(&rcstr!("tsconfig.json")),
211    )?;
212
213    Ok(ResolveOptionsContext {
214        enable_typescript: true,
215        enable_react: true,
216        enable_mjs_extension: true,
217        custom_extensions: next_config.resolve_extension().owned().await?,
218        tsconfig_path: TsConfigHandling::Fixed(tsconfig_path),
219        rules: vec![(
220            foreign_code_context_condition(next_config, project_path).await?,
221            resolve_options_context.clone().resolved_cell(),
222        )],
223        ..resolve_options_context
224    }
225    .cell())
226}
227
228#[turbo_tasks::function]
229pub async fn get_client_module_options_context(
230    project_path: FileSystemPath,
231    execution_context: ResolvedVc<ExecutionContext>,
232    env: ResolvedVc<Environment>,
233    ty: ClientContextType,
234    mode: Vc<NextMode>,
235    next_config: Vc<NextConfig>,
236    encryption_key: ResolvedVc<RcStr>,
237) -> Result<Vc<ModuleOptionsContext>> {
238    let next_mode = mode.await?;
239    let resolve_options_context = get_client_resolve_options_context(
240        project_path.clone(),
241        ty.clone(),
242        mode,
243        next_config,
244        *execution_context,
245    );
246
247    let tsconfig_path = next_config
248        .typescript_tsconfig_path()
249        .await?
250        .as_ref()
251        .map(|p| project_path.join(p))
252        .transpose()?;
253
254    let tsconfig = get_typescript_transform_options(project_path.clone(), tsconfig_path.clone())
255        .to_resolved()
256        .await?;
257    let decorators_options =
258        get_decorators_transform_options(project_path.clone(), tsconfig_path.clone());
259    let enable_mdx_rs = *next_config.mdx_rs().await?;
260    let jsx_runtime_options = get_jsx_transform_options(
261        project_path.clone(),
262        mode,
263        Some(resolve_options_context),
264        false,
265        next_config,
266        tsconfig_path,
267    )
268    .to_resolved()
269    .await?;
270
271    let mut loader_conditions = BTreeSet::new();
272    loader_conditions.insert(WebpackLoaderBuiltinCondition::Browser);
273    loader_conditions.extend(mode.await?.webpack_loader_conditions());
274
275    // A separate webpack rules will be applied to codes matching foreign_code_context_condition.
276    // This allows to import codes from node_modules that requires webpack loaders, which next-dev
277    // implicitly does by default.
278    let mut foreign_conditions = loader_conditions.clone();
279    foreign_conditions.insert(WebpackLoaderBuiltinCondition::Foreign);
280    let foreign_enable_webpack_loaders =
281        *webpack_loader_options(project_path.clone(), next_config, foreign_conditions).await?;
282
283    // Now creates a webpack rules that applies to all code.
284    let enable_webpack_loaders =
285        *webpack_loader_options(project_path.clone(), next_config, loader_conditions).await?;
286
287    let module_fragments_enabled_for_user_code = *next_config
288        .module_fragments_enabled_for_user_code(next_mode.is_development())
289        .await?;
290    let module_fragments_enabled_for_foreign_code = *next_config
291        .module_fragments_enabled_for_foreign_code(next_mode.is_development())
292        .await?;
293    let target_browsers = env.runtime_versions();
294
295    let next_client_rules = get_next_client_transforms_rules(
296        next_config,
297        &project_path,
298        ty.clone(),
299        mode,
300        false,
301        encryption_key,
302        target_browsers,
303    )
304    .await?;
305    let foreign_next_client_rules = get_next_client_transforms_rules(
306        next_config,
307        &project_path,
308        ty.clone(),
309        mode,
310        true,
311        encryption_key,
312        target_browsers,
313    )
314    .await?;
315
316    let local_postcss_config = *next_config
317        .experimental_turbopack_local_postcss_config()
318        .await?;
319    let postcss_config_location = if local_postcss_config == Some(true) {
320        PostCssConfigLocation::LocalPathOrProjectPath
321    } else {
322        PostCssConfigLocation::ProjectPathOrLocalPath
323    };
324    let postcss_transform_options = PostCssTransformOptions {
325        postcss_package: Some(
326            get_postcss_package_mapping(project_path.clone())
327                .to_resolved()
328                .await?,
329        ),
330        config_location: postcss_config_location,
331        ..Default::default()
332    };
333    let postcss_foreign_transform_options = PostCssTransformOptions {
334        // For node_modules we don't want to resolve postcss config relative to the file being
335        // compiled, instead it only uses the project root postcss config.
336        config_location: PostCssConfigLocation::ProjectPath,
337        ..postcss_transform_options.clone()
338    };
339    let enable_postcss_transform = Some(postcss_transform_options.resolved_cell());
340    let enable_foreign_postcss_transform = Some(postcss_foreign_transform_options.resolved_cell());
341
342    let source_maps = *next_config.client_source_maps(mode).await?;
343
344    let preset_env_config = (*next_config.experimental_swc_env_options().await?)
345        .as_ref()
346        .map(|opts| {
347            PresetEnvConfig {
348                mode: opts.mode.clone(),
349                core_js: opts.core_js.clone(),
350                skip: opts.skip.clone(),
351                include: opts.include.clone(),
352                exclude: opts.exclude.clone(),
353                shipped_proposals: opts.shipped_proposals,
354                force_all_transforms: opts.force_all_transforms,
355                debug: opts.debug,
356                loose: opts.loose,
357            }
358            .resolved_cell()
359        });
360
361    let enable_rust_react_compiler = *next_config.rust_react_compiler().await?;
362    let rust_react_compiler_target = if enable_rust_react_compiler.is_some() {
363        match detect_react_compiler_target(&project_path).await? {
364            Some(ReactCompilerTarget::React18) => ReactCompilerTarget::React18,
365            _ => ReactCompilerTarget::React19,
366        }
367    } else {
368        ReactCompilerTarget::React19
369    };
370
371    let module_options_context = ModuleOptionsContext {
372        ecmascript: EcmascriptOptionsContext {
373            esm_url_rewrite_behavior: Some(UrlRewriteBehavior::Relative),
374            enable_typeof_window_inlining: Some(TypeofWindow::Object),
375            enable_import_as_bytes: *next_config.turbopack_import_type_bytes().await?,
376            source_maps,
377            infer_module_side_effects: *next_config.turbopack_infer_module_side_effects().await?,
378            cjs_tree_shaking: *next_config.turbopack_cjs_tree_shaking().await?,
379            mangle_export_names: *next_config.turbopack_mangle_export_names(mode).await?,
380            cjs_scope_hoisting: *next_config.turbopack_cjs_scope_hoisting().await?,
381            cross_module_constants: *next_config.turbopack_cross_module_constants().await?,
382            preset_env_config,
383            ..Default::default()
384        },
385        css: CssOptionsContext {
386            source_maps,
387            module_css_condition: Some(module_styles_rule_condition()),
388            lightningcss_features: *next_config.lightningcss_feature_flags().await?,
389            module_css_debuggable_idents: next_mode.is_development(),
390            ..Default::default()
391        },
392        static_url_tag: Some(rcstr!("client")),
393        environment: Some(env),
394        execution_context: Some(execution_context),
395        follow_reexports: true,
396        module_fragments_enabled: module_fragments_enabled_for_user_code,
397        enable_postcss_transform,
398        side_effect_free_packages: Some(
399            side_effect_free_packages_glob(next_config.optimize_package_imports())
400                .to_resolved()
401                .await?,
402        ),
403        keep_last_successful_parse: next_mode.is_development(),
404        analyze_mode: AnalyzeMode::CodeGeneration,
405        ..Default::default()
406    };
407
408    // node_modules context
409    let foreign_codes_options_context = ModuleOptionsContext {
410        ecmascript: EcmascriptOptionsContext {
411            enable_typeof_window_inlining: None,
412            // Ignore e.g. import(`${url}`) requests in node_modules.
413            ignore_dynamic_requests: true,
414            // Don't inject core-js polyfills into node_modules — only user code
415            // should be processed by preset_env's usage/entry mode.
416            preset_env_config: None,
417            ..module_options_context.ecmascript
418        },
419        enable_webpack_loaders: foreign_enable_webpack_loaders,
420        enable_postcss_transform: enable_foreign_postcss_transform,
421        module_rules: foreign_next_client_rules,
422        follow_reexports: true,
423        module_fragments_enabled: module_fragments_enabled_for_foreign_code,
424        // NOTE(WEB-1016) PostCSS transforms should also apply to foreign code.
425        ..module_options_context.clone()
426    };
427
428    let internal_context = ModuleOptionsContext {
429        ecmascript: EcmascriptOptionsContext {
430            enable_typescript_transform: Some(
431                TypescriptTransformOptions::default().resolved_cell(),
432            ),
433            enable_jsx: Some(JsxTransformOptions::default().resolved_cell()),
434            // Don't inject core-js polyfills into framework internals.
435            preset_env_config: None,
436            ..module_options_context.ecmascript.clone()
437        },
438        enable_postcss_transform: None,
439        ..module_options_context.clone()
440    };
441
442    let module_options_context = ModuleOptionsContext {
443        // We don't need to resolve React Refresh for each module. Instead,
444        // we try resolve it once at the root and pass down a context to all
445        // the modules.
446        ecmascript: EcmascriptOptionsContext {
447            enable_jsx: Some(jsx_runtime_options),
448            enable_typescript_transform: Some(tsconfig),
449            enable_decorators: Some(decorators_options.to_resolved().await?),
450            enable_rust_react_compiler,
451            rust_react_compiler_target,
452            ..module_options_context.ecmascript.clone()
453        },
454        enable_webpack_loaders,
455        enable_mdx_rs,
456        rules: vec![
457            (
458                foreign_code_context_condition(next_config, project_path).await?,
459                foreign_codes_options_context.resolved_cell(),
460            ),
461            (
462                internal_assets_conditions().await?,
463                internal_context.resolved_cell(),
464            ),
465        ],
466        module_rules: next_client_rules,
467        ..module_options_context
468    }
469    .cell();
470
471    Ok(module_options_context)
472}
473
474#[turbo_tasks::task_input(contains_unresolved_vcs)]
475#[derive(Clone, Debug, PartialEq, Eq, Hash, TraceRawVcs, Encode, Decode)]
476pub struct ClientChunkingContextOptions {
477    pub mode: Vc<NextMode>,
478    pub root_path: FileSystemPath,
479    pub client_root: FileSystemPath,
480    pub client_root_to_root_path: RcStr,
481    pub client_static_folder_name: RcStr,
482    pub asset_prefix: Vc<RcStr>,
483    pub service_worker_scope_base_path: Vc<Option<RcStr>>,
484    pub environment: Vc<Environment>,
485    pub module_id_strategy: Vc<ModuleIdStrategy>,
486    pub export_usage: Vc<OptionBindingUsageInfo>,
487    pub unused_references: Vc<UnusedReferences>,
488    pub minify: Vc<bool>,
489    pub source_maps: Vc<SourceMapsType>,
490    pub no_mangling: Vc<bool>,
491    pub scope_hoisting: Vc<bool>,
492    pub nested_async_chunking: Vc<bool>,
493    pub shared_runtime: Vc<bool>,
494    pub per_page_module_graph: Vc<bool>,
495    pub debug_ids: Vc<bool>,
496    pub worker_asset_prefix: Vc<Option<RcStr>>,
497    pub should_use_absolute_url_references: Vc<bool>,
498    pub css_url_suffix: Vc<Option<RcStr>>,
499    pub hash_salt: ResolvedVc<RcStr>,
500    pub cross_origin: Vc<CrossOrigin>,
501    pub chunk_loading_global: Vc<Option<RcStr>>,
502    pub style_groups_algorithm: StyleGroupsAlgorithm,
503    pub chunking_first_page_load_priority: Option<u32>,
504    pub chunking_priority_boost_percent: Option<u32>,
505    pub chunking_request_cost: Option<u64>,
506    pub chunking_min_chunk_size: Option<usize>,
507    pub chunking_max_chunk_count_per_group: Option<usize>,
508    pub chunking_max_merge_chunk_size: Option<usize>,
509    pub chunking_min_component_chunk_size: Option<usize>,
510    pub generate_component_chunks: Vc<bool>,
511}
512
513/// Next.js' chunk-load retry policy for the Turbopack browser runtime.
514/// Webpack does not currently support chunk-load retrying.
515const NEXT_CHUNK_LOAD_RETRY: ChunkLoadRetry = ChunkLoadRetry {
516    max_retry_attempts: 1,
517    base_delay_ms: 200,
518    max_jitter_ms: 400,
519};
520
521#[turbo_tasks::function]
522pub async fn get_client_chunking_context(
523    options: ClientChunkingContextOptions,
524) -> Result<Vc<Box<dyn ChunkingContext>>> {
525    let ClientChunkingContextOptions {
526        mode,
527        root_path,
528        client_root,
529        client_root_to_root_path,
530        client_static_folder_name,
531        asset_prefix,
532        service_worker_scope_base_path,
533        environment,
534        module_id_strategy,
535        export_usage,
536        unused_references,
537        minify,
538        source_maps,
539        no_mangling,
540        scope_hoisting,
541        nested_async_chunking,
542        shared_runtime,
543        per_page_module_graph,
544        debug_ids,
545        worker_asset_prefix,
546        should_use_absolute_url_references,
547        css_url_suffix,
548        hash_salt,
549        cross_origin,
550        chunk_loading_global,
551        style_groups_algorithm,
552        chunking_first_page_load_priority,
553        chunking_priority_boost_percent,
554        chunking_request_cost,
555        chunking_min_chunk_size,
556        chunking_max_chunk_count_per_group,
557        chunking_max_merge_chunk_size,
558        chunking_min_component_chunk_size,
559        generate_component_chunks,
560    } = options;
561
562    let next_mode = mode.await?;
563    let asset_prefix = asset_prefix.owned().await?;
564    let service_worker_scope_base_path = service_worker_scope_base_path.owned().await?;
565    let cross_origin_loading = *cross_origin.await?;
566    let mut builder = BrowserChunkingContext::builder(
567        root_path,
568        client_root.clone(),
569        client_root_to_root_path,
570        client_root.clone(),
571        client_root
572            .join(&client_static_folder_name)?
573            .join("chunks")?,
574        client_root
575            .join(&client_static_folder_name)?
576            .join("media")?,
577        environment.to_resolved().await?,
578        next_mode.runtime_type(),
579    )
580    .chunk_base_path(Some(asset_prefix.clone()))
581    .service_worker_scope_base_path(service_worker_scope_base_path)
582    .asset_suffix(AssetSuffix::Inferred.resolved_cell())
583    .minify_type(if *minify.await? {
584        MinifyType::Minify {
585            mangle: (!*no_mangling.await?).then_some(MangleType::OptimalSize),
586        }
587    } else {
588        MinifyType::NoMinify
589    })
590    .source_maps(*source_maps.await?)
591    .asset_base_path(Some(asset_prefix))
592    .current_chunk_method(CurrentChunkMethod::DocumentCurrentScript)
593    .cross_origin(cross_origin_loading)
594    .chunk_load_retry(NEXT_CHUNK_LOAD_RETRY)
595    .export_usage(*export_usage.await?)
596    .unused_references(unused_references.to_resolved().await?)
597    .module_id_strategy(module_id_strategy.to_resolved().await?)
598    .debug_ids(*debug_ids.await?)
599    .worker_asset_prefix(worker_asset_prefix.owned().await?)
600    .should_use_absolute_url_references(*should_use_absolute_url_references.await?)
601    .nested_async_availability(*nested_async_chunking.await?)
602    .worker_forwarded_globals(worker_forwarded_globals())
603    .hash_salt(hash_salt)
604    .default_url_behavior(UrlBehavior {
605        suffix: AssetSuffix::Inferred,
606        static_suffix: css_url_suffix.to_resolved().await?,
607    });
608
609    if let Some(g) = &*chunk_loading_global.await? {
610        builder = builder.chunk_loading_global(g.clone());
611    }
612
613    // Per-page graphs each see only one page, so none of them can decide what the shared runtime
614    // chunk may leave out.
615    builder = builder.shared_runtime_chunk(*per_page_module_graph.await?);
616
617    if next_mode.is_development() {
618        builder = builder
619            .hot_module_replacement()
620            .source_map_source_type(SourceMapSourceType::AbsoluteFileUri)
621            .dynamic_chunk_content_loading(true);
622    } else {
623        builder = builder
624            .chunking_config(
625                Vc::<EcmascriptChunkType>::default().to_resolved().await?,
626                ChunkingConfig {
627                    min_chunk_size: chunking_min_chunk_size.unwrap_or(50_000),
628                    max_chunk_count_per_group: chunking_max_chunk_count_per_group.unwrap_or(40),
629                    max_merge_chunk_size: chunking_max_merge_chunk_size.unwrap_or(200_000),
630                    first_page_load_priority: chunking_first_page_load_priority,
631                    priority_boost_percent: chunking_priority_boost_percent,
632                    request_cost: chunking_request_cost,
633                    // Generate component chunks alongside the merged chunk so that the browser
634                    // runtime can fetch an already-cached one instead of the whole merged chunk.
635                    generate_component_chunks: *generate_component_chunks.await?,
636                    min_component_chunk_size: chunking_min_component_chunk_size.unwrap_or(20_000),
637                    ..Default::default()
638                },
639            )
640            .chunking_config(
641                Vc::<CssChunkType>::default().to_resolved().await?,
642                ChunkingConfig {
643                    max_merge_chunk_size: 100_000,
644                    style_groups_algorithm: style_groups_algorithm.clone(),
645                    ..Default::default()
646                },
647            )
648            .chunk_content_hashing(ContentHashing::Direct { length: 13 })
649            .module_merging(*scope_hoisting.await?)
650            .shared_runtime(*shared_runtime.await?);
651    }
652
653    Ok(Vc::upcast(builder.build()))
654}
655
656#[turbo_tasks::task_input(contains_unresolved_vcs)]
657#[derive(Clone, Debug, PartialEq, Eq, Hash, TraceRawVcs, Encode, Decode)]
658pub struct ServiceWorkerChunkingContextOptions {
659    pub mode: Vc<NextMode>,
660    pub root_path: FileSystemPath,
661    pub output_root: FileSystemPath,
662    pub output_root_to_root_path: RcStr,
663    pub environment: Vc<Environment>,
664    pub minify: Vc<bool>,
665    pub source_maps: Vc<SourceMapsType>,
666    pub no_mangling: Vc<bool>,
667    pub hash_salt: ResolvedVc<RcStr>,
668}
669
670#[turbo_tasks::function]
671pub async fn get_service_worker_chunking_context(
672    options: ServiceWorkerChunkingContextOptions,
673) -> Result<Vc<Box<dyn ChunkingContext>>> {
674    let ServiceWorkerChunkingContextOptions {
675        mode,
676        root_path,
677        output_root,
678        output_root_to_root_path,
679        environment,
680        minify,
681        source_maps,
682        no_mangling,
683        hash_salt,
684    } = options;
685
686    let next_mode = mode.await?;
687    let builder = BrowserChunkingContext::builder(
688        root_path,
689        output_root.clone(),
690        output_root_to_root_path,
691        output_root.clone(),
692        output_root.join("chunks")?,
693        output_root.join("media")?,
694        environment.to_resolved().await?,
695        next_mode.runtime_type(),
696    )
697    .current_chunk_method(CurrentChunkMethod::StringLiteral)
698    .asset_suffix(AssetSuffix::None.resolved_cell())
699    .minify_type(if *minify.await? {
700        MinifyType::Minify {
701            mangle: (!*no_mangling.await?).then_some(MangleType::OptimalSize),
702        }
703    } else {
704        MinifyType::NoMinify
705    })
706    .source_maps(*source_maps.await?)
707    .hash_salt(hash_salt)
708    .single_chunk()
709    .await?;
710
711    Ok(Vc::upcast(builder.build()))
712}
713
714#[turbo_tasks::function]
715pub async fn get_client_runtime_entries(
716    project_root: FileSystemPath,
717    ty: ClientContextType,
718    mode: Vc<NextMode>,
719    next_config: Vc<NextConfig>,
720    execution_context: Vc<ExecutionContext>,
721) -> Result<Vc<RuntimeEntries>> {
722    let mut runtime_entries = vec![];
723    let resolve_options_context = get_client_resolve_options_context(
724        project_root.clone(),
725        ty.clone(),
726        mode,
727        next_config,
728        execution_context,
729    );
730
731    if mode.await?.is_development() {
732        let enable_react_refresh =
733            assert_can_resolve_react_refresh(project_root.clone(), resolve_options_context)
734                .await?
735                .as_request();
736
737        // It's important that React Refresh come before the regular bootstrap file,
738        // because the bootstrap contains JSX which requires Refresh's global
739        // functions to be available.
740        if let Some(request) = enable_react_refresh {
741            runtime_entries.push(
742                RuntimeEntry::Request(request.to_resolved().await?, project_root.join("_")?)
743                    .resolved_cell(),
744            )
745        };
746    }
747
748    if matches!(ty, ClientContextType::App { .. },) {
749        runtime_entries.push(
750            RuntimeEntry::Request(
751                Request::parse(Pattern::Constant(rcstr!(
752                    "next/dist/client/app-next-turbopack.js"
753                )))
754                .to_resolved()
755                .await?,
756                project_root.join("_")?,
757            )
758            .resolved_cell(),
759        );
760    }
761
762    Ok(Vc::cell(runtime_entries))
763}