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