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