Skip to main content

next_core/next_server/
context.rs

1use std::collections::BTreeSet;
2
3use anyhow::{Result, bail};
4use bincode::{Decode, Encode};
5use turbo_rcstr::{RcStr, rcstr};
6use turbo_tasks::{ResolvedVc, Vc, trace::TraceRawVcs};
7use turbo_tasks_fs::FileSystemPath;
8use turbopack::{
9    module_options::{
10        CssOptionsContext, EcmascriptOptionsContext, ExternalsTracingOptions, JsxTransformOptions,
11        ModuleOptionsContext, ModuleRule, TypescriptTransformOptions,
12        side_effect_free_packages_glob,
13    },
14    transition::Transition,
15};
16use turbopack_core::{
17    chunk::{
18        AssetSuffix, ChunkingConfig, MangleType, MinifyType, SourceMapSourceType, SourceMapsType,
19        UnusedReferences, UrlBehavior, chunk_id_strategy::ModuleIdStrategy,
20    },
21    compile_time_defines,
22    compile_time_info::{CompileTimeDefines, CompileTimeInfo, FreeVarReferences},
23    environment::{Environment, ExecutionEnvironment, NodeJsEnvironment, NodeJsVersion},
24    issue::IssueSeverity,
25    module_graph::{
26        binding_usage_info::OptionBindingUsageInfo, style_groups::StyleGroupsAlgorithm,
27    },
28    target::CompileTarget,
29};
30use turbopack_css::chunk::CssChunkType;
31use turbopack_ecmascript::{
32    AnalyzeMode, CustomTransformer, TransformPlugin, TypeofWindow, chunk::EcmascriptChunkType,
33    references::esm::UrlRewriteBehavior, transform::ReactCompilerTarget,
34};
35use turbopack_ecmascript_plugins::transform::directives::{
36    client::ClientDirectiveTransformer, client_disallowed::ClientDisallowedDirectiveTransformer,
37};
38use turbopack_node::{
39    execution_context::ExecutionContext,
40    transforms::postcss::{PostCssConfigLocation, PostCssTransformOptions},
41};
42use turbopack_nodejs::NodeJsChunkingContext;
43use turbopack_resolve::resolve_options_context::{ResolveOptionsContext, TsConfigHandling};
44
45use crate::{
46    app_structure::CollectedRootParams,
47    mode::NextMode,
48    next_build::get_postcss_package_mapping,
49    next_config::NextConfig,
50    next_font::local::NextFontLocalResolvePlugin,
51    next_import_map::{get_next_edge_and_server_fallback_import_map, get_next_server_import_map},
52    next_server::{
53        resolve::{ExternalCjsModulesResolvePlugin, ExternalPredicate},
54        transforms::{get_next_server_internal_transforms_rules, get_next_server_transforms_rules},
55    },
56    next_shared::{
57        resolve::{NextExternalResolvePlugin, NextNodeSharedRuntimeResolvePlugin},
58        transforms::{
59            EcmascriptTransformStage, emotion::get_emotion_transform_rule, get_ecma_transform_rule,
60            next_react_server_components::get_next_react_server_components_transform_rule,
61            react_remove_properties::get_react_remove_properties_transform_rule,
62            relay::get_relay_transform_rule, remove_console::get_remove_console_transform_rule,
63            styled_components::get_styled_components_transform_rule,
64            styled_jsx::get_styled_jsx_transform_rule,
65            swc_ecma_transform_plugins::get_swc_ecma_transform_plugin_rule,
66        },
67        webpack_rules::{
68            WebpackLoaderBuiltinCondition, babel::detect_react_compiler_target,
69            webpack_loader_options,
70        },
71    },
72    transform_options::{
73        get_decorators_transform_options, get_jsx_transform_options,
74        get_typescript_transform_options,
75    },
76    util::{
77        NextRuntime, OptionEnvMap, defines, foreign_code_context_condition,
78        free_var_references_with_vercel_system_env_warnings, get_transpiled_packages,
79        internal_assets_conditions, load_next_js_jsonc_file, module_styles_rule_condition,
80        worker_forwarded_globals,
81    },
82};
83
84#[turbo_tasks::value(shared, task_input)]
85#[derive(Debug, Clone, Hash)]
86pub enum ServerContextType {
87    Pages {
88        pages_dir: FileSystemPath,
89    },
90    PagesApi {
91        pages_dir: FileSystemPath,
92    },
93    AppSSR {
94        app_dir: FileSystemPath,
95    },
96    AppRSC {
97        app_dir: FileSystemPath,
98        ecmascript_client_reference_transition_name: Option<RcStr>,
99        client_transition: Option<ResolvedVc<Box<dyn Transition>>>,
100    },
101    AppRoute {
102        app_dir: FileSystemPath,
103        ecmascript_client_reference_transition_name: Option<RcStr>,
104    },
105    Middleware {
106        app_dir: Option<FileSystemPath>,
107        ecmascript_client_reference_transition_name: Option<RcStr>,
108    },
109    Instrumentation {
110        app_dir: Option<FileSystemPath>,
111        ecmascript_client_reference_transition_name: Option<RcStr>,
112    },
113}
114
115impl ServerContextType {
116    pub fn should_use_react_server_condition(&self) -> bool {
117        matches!(
118            self,
119            ServerContextType::AppRSC { .. }
120                | ServerContextType::AppRoute { .. }
121                | ServerContextType::Middleware { .. }
122                | ServerContextType::Instrumentation { .. }
123        )
124    }
125}
126
127#[turbo_tasks::function]
128pub async fn get_server_resolve_options_context(
129    project_path: FileSystemPath,
130    ty: ServerContextType,
131    mode: Vc<NextMode>,
132    next_config: Vc<NextConfig>,
133    execution_context: Vc<ExecutionContext>,
134    collected_root_params: Option<Vc<CollectedRootParams>>,
135) -> Result<Vc<ResolveOptionsContext>> {
136    let next_server_import_map = get_next_server_import_map(
137        project_path.clone(),
138        ty.clone(),
139        next_config,
140        mode,
141        execution_context,
142        collected_root_params,
143    )
144    .to_resolved()
145    .await?;
146    let next_server_fallback_import_map =
147        get_next_edge_and_server_fallback_import_map(project_path.clone(), NextRuntime::NodeJs)
148            .to_resolved()
149            .await?;
150
151    let foreign_code_context_condition =
152        foreign_code_context_condition(next_config, project_path.clone()).await?;
153    let root_dir = project_path.root().owned().await?;
154
155    // Always load these predefined packages as external.
156    let mut external_packages: Vec<RcStr> = load_next_js_jsonc_file(
157        project_path.clone(),
158        rcstr!("dist/lib/server-external-packages.jsonc"),
159    )
160    .await?;
161
162    let mut transpiled_packages = get_transpiled_packages(next_config, project_path.clone())
163        .owned()
164        .await?;
165
166    transpiled_packages.extend(
167        (*next_config.optimize_package_imports().await?)
168            .iter()
169            .cloned(),
170    );
171
172    let server_external_packages = &*next_config.server_external_packages().await?;
173
174    let conflicting_packages = transpiled_packages
175        .iter()
176        .filter(|package| server_external_packages.contains(package))
177        .collect::<Vec<_>>();
178
179    if !conflicting_packages.is_empty() {
180        bail!(
181            "The packages specified in the 'transpilePackages' conflict with the \
182             'serverExternalPackages': {:?}",
183            conflicting_packages
184        );
185    }
186
187    // Add the config's own list of external packages.
188    external_packages.extend(server_external_packages.iter().cloned());
189
190    external_packages.retain(|item| !transpiled_packages.contains(item));
191
192    let server_external_packages_plugin = ExternalCjsModulesResolvePlugin::new(
193        project_path.root().owned().await?,
194        ExternalPredicate::Only(ResolvedVc::cell(external_packages)).cell(),
195        *next_config.import_externals().await?,
196    )
197    .to_resolved()
198    .await?;
199
200    let mut custom_conditions: Vec<_> = mode.await?.custom_resolve_conditions().collect();
201    custom_conditions.extend(NextRuntime::NodeJs.custom_resolve_conditions());
202
203    if ty.should_use_react_server_condition() {
204        custom_conditions.push(rcstr!("react-server"));
205    };
206
207    if *next_config.enable_cache_components().await?
208        // Middleware shouldn't use the "next-js" condition because it doesn't have all Next.js APIs available
209        && !matches!(ty, ServerContextType::Middleware { .. } |  ServerContextType::Instrumentation { .. })
210    {
211        custom_conditions.push(rcstr!("next-js"));
212    };
213
214    let external_cjs_modules_plugin = if *next_config.bundle_pages_router_dependencies().await? {
215        server_external_packages_plugin
216    } else {
217        ExternalCjsModulesResolvePlugin::new(
218            project_path.root().owned().await?,
219            ExternalPredicate::AllExcept(ResolvedVc::cell(transpiled_packages)).cell(),
220            *next_config.import_externals().await?,
221        )
222        .to_resolved()
223        .await?
224    };
225
226    let next_external_plugin = NextExternalResolvePlugin::new(project_path.clone())
227        .to_resolved()
228        .await?;
229    let next_node_shared_runtime_plugin =
230        NextNodeSharedRuntimeResolvePlugin::new(project_path.clone(), ty.clone())
231            .to_resolved()
232            .await?;
233
234    let before_resolve_plugins = match &ty {
235        ServerContextType::Pages { .. }
236        | ServerContextType::AppSSR { .. }
237        | ServerContextType::AppRSC { .. } => {
238            vec![ResolvedVc::upcast(
239                NextFontLocalResolvePlugin::new(project_path.clone())
240                    .to_resolved()
241                    .await?,
242            )]
243        }
244        ServerContextType::PagesApi { .. }
245        | ServerContextType::AppRoute { .. }
246        | ServerContextType::Middleware { .. }
247        | ServerContextType::Instrumentation { .. } => {
248            vec![]
249        }
250    };
251
252    let after_resolve_plugins = match ty {
253        ServerContextType::Pages { .. } | ServerContextType::PagesApi { .. } => {
254            vec![
255                ResolvedVc::upcast(next_node_shared_runtime_plugin),
256                ResolvedVc::upcast(external_cjs_modules_plugin),
257                ResolvedVc::upcast(next_external_plugin),
258            ]
259        }
260        ServerContextType::AppSSR { .. }
261        | ServerContextType::AppRSC { .. }
262        | ServerContextType::AppRoute { .. } => {
263            vec![
264                ResolvedVc::upcast(next_node_shared_runtime_plugin),
265                ResolvedVc::upcast(server_external_packages_plugin),
266                ResolvedVc::upcast(next_external_plugin),
267            ]
268        }
269        ServerContextType::Middleware { .. } | ServerContextType::Instrumentation { .. } => {
270            vec![
271                ResolvedVc::upcast(next_node_shared_runtime_plugin),
272                ResolvedVc::upcast(server_external_packages_plugin),
273                ResolvedVc::upcast(next_external_plugin),
274            ]
275        }
276    };
277
278    let resolve_options_context = ResolveOptionsContext {
279        enable_node_modules: Some(root_dir.clone()),
280        enable_node_externals: true,
281        enable_node_native_modules: true,
282        module: true,
283        custom_conditions,
284        import_map: Some(next_server_import_map),
285        fallback_import_map: Some(next_server_fallback_import_map),
286        before_resolve_plugins,
287        after_resolve_plugins,
288        ..Default::default()
289    };
290
291    let tsconfig_path = next_config.typescript_tsconfig_path().await?;
292    let tsconfig_path = project_path.join(
293        tsconfig_path
294            .as_ref()
295            // Fall back to tsconfig only for resolving. This is because we don't want Turbopack to
296            // resolve tsconfig.json relative to the file being compiled.
297            .unwrap_or(&rcstr!("tsconfig.json")),
298    )?;
299
300    Ok(ResolveOptionsContext {
301        enable_typescript: true,
302        enable_react: true,
303        enable_mjs_extension: true,
304        custom_extensions: next_config.resolve_extension().owned().await?,
305        tsconfig_path: TsConfigHandling::Fixed(tsconfig_path),
306        rules: vec![(
307            foreign_code_context_condition,
308            resolve_options_context.clone().resolved_cell(),
309        )],
310        ..resolve_options_context
311    }
312    .cell())
313}
314
315#[turbo_tasks::function]
316async fn next_server_defines(define_env: Vc<OptionEnvMap>) -> Result<Vc<CompileTimeDefines>> {
317    Ok(defines(&*define_env.await?).cell())
318}
319
320#[turbo_tasks::function]
321async fn next_server_free_vars(
322    define_env: Vc<OptionEnvMap>,
323    report_system_env_inlining: Vc<IssueSeverity>,
324) -> Result<Vc<FreeVarReferences>> {
325    Ok(free_var_references_with_vercel_system_env_warnings(
326        defines(&*define_env.await?),
327        *report_system_env_inlining.await?,
328    )
329    .cell())
330}
331
332#[turbo_tasks::function]
333pub async fn get_server_compile_time_info(
334    cwd: Vc<FileSystemPath>,
335    define_env: Vc<OptionEnvMap>,
336    node_version: ResolvedVc<NodeJsVersion>,
337    report_system_env_inlining: Vc<IssueSeverity>,
338    hot_module_replacement_enabled: bool,
339) -> Result<Vc<CompileTimeInfo>> {
340    CompileTimeInfo::builder(
341        Environment::new(ExecutionEnvironment::NodeJsLambda(
342            NodeJsEnvironment {
343                compile_target: CompileTarget::current().to_resolved().await?,
344                node_version,
345                cwd: ResolvedVc::cell(Some(cwd.owned().await?)),
346            }
347            .resolved_cell(),
348        ))
349        .to_resolved()
350        .await?,
351    )
352    .defines(next_server_defines(define_env).to_resolved().await?)
353    .free_var_references(
354        next_server_free_vars(define_env, report_system_env_inlining)
355            .to_resolved()
356            .await?,
357    )
358    .hot_module_replacement_enabled(hot_module_replacement_enabled)
359    .cell()
360    .await
361}
362
363#[turbo_tasks::function]
364pub async fn get_tracing_compile_time_info() -> Result<Vc<CompileTimeInfo>> {
365    CompileTimeInfo::builder(
366        Environment::new(ExecutionEnvironment::NodeJsLambda(
367            NodeJsEnvironment::default().resolved_cell(),
368        ))
369        .to_resolved()
370        .await?,
371    )
372    /*
373    We'd really like to set `process.env.NODE_ENV = "production"` here, but with that,
374    `react/cjs/react.development.js` won't be copied anymore (as expected).
375    However if you `import` react from native ESM: `import {createContext} from 'react';`, it fails with
376    ```
377    import {createContext} from 'react';
378            ^^^^^^^^^^^^^
379    SyntaxError: Named export 'createContext' not found. The requested module 'react' is a CommonJS module, which may not support all module.exports as named exports.
380    CommonJS modules can always be imported via the default export, for example using:
381    ```
382    This is because Node's import-cjs-from-esm feature can correctly find all named exports in
383    ```
384    // `react/index.js`
385    if (process.env.NODE_ENV === 'production') {
386      module.exports = require('./cjs/react.production.js');
387    } else {
388      module.exports = require('./cjs/react.development.js');
389    }
390    ```
391    if both files exist (which is what's happening so far).
392    If `react.development.js` doesn't exist, then it bails with that error message.
393    Also just removing that second branch works fine, but a `require` to a non-existent file fails.
394    */
395    .defines(
396        compile_time_defines!(
397            process.env.TURBOPACK = "1",
398            // process.env.NODE_ENV = "production",
399        )
400        .resolved_cell(),
401    )
402    .cell()
403    .await
404}
405
406#[turbo_tasks::function]
407pub async fn get_server_module_options_context(
408    project_path: FileSystemPath,
409    execution_context: ResolvedVc<ExecutionContext>,
410    ty: ServerContextType,
411    mode: Vc<NextMode>,
412    next_config: Vc<NextConfig>,
413    next_runtime: NextRuntime,
414    encryption_key: ResolvedVc<RcStr>,
415    environment: ResolvedVc<Environment>,
416    client_environment: ResolvedVc<Environment>,
417    enable_tracing: bool,
418) -> Result<Vc<ModuleOptionsContext>> {
419    let next_mode = mode.await?;
420    let mut next_server_rules = get_next_server_transforms_rules(
421        next_config,
422        ty.clone(),
423        mode,
424        false,
425        next_runtime,
426        encryption_key,
427    )
428    .await?;
429    let mut foreign_next_server_rules = get_next_server_transforms_rules(
430        next_config,
431        ty.clone(),
432        mode,
433        true,
434        next_runtime,
435        encryption_key,
436    )
437    .await?;
438    let mut internal_custom_rules = get_next_server_internal_transforms_rules(
439        ty.clone(),
440        next_config.mdx_rs().await?.is_some(),
441    )
442    .await?;
443
444    let foreign_code_context_condition =
445        foreign_code_context_condition(next_config, project_path.clone()).await?;
446    let local_postcss_config = *next_config
447        .experimental_turbopack_local_postcss_config()
448        .await?;
449    let postcss_config_location = if local_postcss_config == Some(true) {
450        PostCssConfigLocation::LocalPathOrProjectPath
451    } else {
452        PostCssConfigLocation::ProjectPathOrLocalPath
453    };
454    let postcss_transform_options = PostCssTransformOptions {
455        postcss_package: Some(
456            get_postcss_package_mapping(project_path.clone())
457                .to_resolved()
458                .await?,
459        ),
460        config_location: postcss_config_location,
461        ..Default::default()
462    };
463    let postcss_foreign_transform_options = PostCssTransformOptions {
464        // For node_modules we don't want to resolve postcss config relative to the file
465        // being compiled, instead it only uses the project root postcss
466        // config.
467        config_location: PostCssConfigLocation::ProjectPath,
468        ..postcss_transform_options.clone()
469    };
470    let enable_postcss_transform = Some(postcss_transform_options.resolved_cell());
471    let enable_foreign_postcss_transform = Some(postcss_foreign_transform_options.resolved_cell());
472
473    let mut loader_conditions = BTreeSet::new();
474    loader_conditions.extend(mode.await?.webpack_loader_conditions());
475    loader_conditions.extend(next_runtime.webpack_loader_conditions());
476
477    // A separate webpack rules will be applied to codes matching foreign_code_context_condition.
478    // This allows to import codes from node_modules that requires webpack loaders, which next-dev
479    // implicitly does by default.
480    let mut foreign_conditions = loader_conditions.clone();
481    foreign_conditions.insert(WebpackLoaderBuiltinCondition::Foreign);
482    let foreign_enable_webpack_loaders =
483        *webpack_loader_options(project_path.clone(), next_config, foreign_conditions).await?;
484
485    // Now creates a webpack rules that applies to all code.
486    let enable_webpack_loaders =
487        *webpack_loader_options(project_path.clone(), next_config, loader_conditions).await?;
488
489    let module_fragments_enabled_for_user_code = *next_config
490        .module_fragments_enabled_for_user_code(next_mode.is_development())
491        .await?;
492    let module_fragments_enabled_for_foreign_code = *next_config
493        .module_fragments_enabled_for_foreign_code(next_mode.is_development())
494        .await?;
495
496    let tsconfig_path = next_config
497        .typescript_tsconfig_path()
498        .await?
499        .as_ref()
500        .map(|p| project_path.join(p))
501        .transpose()?;
502
503    // ModuleOptionsContext related options
504    let tsconfig = get_typescript_transform_options(project_path.clone(), tsconfig_path.clone())
505        .to_resolved()
506        .await?;
507    let decorators_options =
508        get_decorators_transform_options(project_path.clone(), tsconfig_path.clone());
509    let enable_mdx_rs = *next_config.mdx_rs().await?;
510
511    // Get the jsx transform options for the `client` side.
512    // This matches to the behavior of existing webpack config, if issuer layer is
513    // ssr or pages-browser (client bundle for the browser)
514    // applies client specific swc transforms.
515    //
516    // This enables correct emotion transform and other hydration between server and
517    // client bundles. ref: https://github.com/vercel/next.js/blob/4bbf9b6c70d2aa4237defe2bebfa790cdb7e334e/packages/next/src/build/webpack-config.ts#L1421-L1426
518    let jsx_runtime_options = get_jsx_transform_options(
519        project_path.clone(),
520        mode,
521        None,
522        false,
523        next_config,
524        tsconfig_path.clone(),
525    )
526    .to_resolved()
527    .await?;
528    let rsc_jsx_runtime_options = get_jsx_transform_options(
529        project_path.clone(),
530        mode,
531        None,
532        true,
533        next_config,
534        tsconfig_path,
535    )
536    .to_resolved()
537    .await?;
538
539    // A set of custom ecma transform rules being applied to server context.
540    let source_transform_rules: Vec<ModuleRule> = vec![
541        get_remove_console_transform_rule(next_config).await?,
542        get_react_remove_properties_transform_rule(next_config).await?,
543        get_emotion_transform_rule(next_config).await?,
544        get_relay_transform_rule(next_config, project_path.clone()).await?,
545        get_swc_ecma_transform_plugin_rule(next_config, project_path.clone()).await?,
546    ]
547    .into_iter()
548    .flatten()
549    .collect();
550
551    // Only relevant for pages, not routes/etc.
552    let page_transform_rules: Vec<ModuleRule> = vec![
553        get_styled_components_transform_rule(next_config).await?,
554        // It's important the client's browserlist config is used for styled-jsx, otherwise we
555        // transpile the CSS to be compatible with Node.js 20.
556        get_styled_jsx_transform_rule(next_config, client_environment.runtime_versions()).await?,
557    ]
558    .into_iter()
559    .flatten()
560    .collect();
561
562    let enable_rust_react_compiler = *next_config.rust_react_compiler().await?;
563    let rust_react_compiler_target = if enable_rust_react_compiler.is_some() {
564        match detect_react_compiler_target(&project_path).await? {
565            Some(ReactCompilerTarget::React18) => ReactCompilerTarget::React18,
566            _ => ReactCompilerTarget::React19,
567        }
568    } else {
569        ReactCompilerTarget::React19
570    };
571
572    let source_maps = *next_config.server_source_maps().await?;
573    let module_options_context = ModuleOptionsContext {
574        ecmascript: EcmascriptOptionsContext {
575            enable_typeof_window_inlining: Some(TypeofWindow::Undefined),
576            enable_import_as_bytes: *next_config.turbopack_import_type_bytes().await?,
577            import_externals: *next_config.import_externals().await?,
578            ignore_dynamic_requests: true,
579            source_maps,
580            infer_module_side_effects: *next_config.turbopack_infer_module_side_effects().await?,
581            cjs_tree_shaking: *next_config.turbopack_cjs_tree_shaking().await?,
582            ..Default::default()
583        },
584        execution_context: Some(execution_context),
585        environment: Some(environment),
586        css: CssOptionsContext {
587            source_maps,
588            module_css_condition: Some(module_styles_rule_condition()),
589            lightningcss_features: *next_config.lightningcss_feature_flags().await?,
590            ..Default::default()
591        },
592        follow_reexports: true,
593        module_fragments_enabled: module_fragments_enabled_for_user_code,
594        side_effect_free_packages: Some(
595            side_effect_free_packages_glob(next_config.optimize_package_imports())
596                .to_resolved()
597                .await?,
598        ),
599        analyze_mode: if enable_tracing {
600            AnalyzeMode::CodeGenerationAndTracing
601        } else {
602            AnalyzeMode::CodeGeneration
603        },
604        enable_externals_tracing: if enable_tracing {
605            Some(
606                ExternalsTracingOptions {
607                    tracing_root: project_path,
608                    compile_time_info: get_tracing_compile_time_info().to_resolved().await?,
609                }
610                .resolved_cell(),
611            )
612        } else {
613            None
614        },
615        keep_last_successful_parse: next_mode.is_development(),
616
617        ..Default::default()
618    };
619
620    let module_options_context = match ty {
621        ServerContextType::Pages { .. } | ServerContextType::PagesApi { .. } => {
622            next_server_rules.extend(source_transform_rules);
623            if let ServerContextType::Pages { .. } = ty {
624                next_server_rules.push(
625                    get_next_react_server_components_transform_rule(next_config, false, None)
626                        .await?,
627                );
628            }
629            next_server_rules.extend(page_transform_rules);
630
631            foreign_next_server_rules.extend(internal_custom_rules);
632
633            let (url_rewrite_behavior, static_url_tag) = {
634                //https://github.com/vercel/next.js/blob/bbb730e5ef10115ed76434f250379f6f53efe998/packages/next/src/build/webpack-config.ts#L1384
635                if let ServerContextType::PagesApi { .. } = ty {
636                    (Some(UrlRewriteBehavior::Full), None)
637                } else {
638                    (Some(UrlRewriteBehavior::Relative), Some(rcstr!("client")))
639                }
640            };
641
642            let module_options_context = ModuleOptionsContext {
643                ecmascript: EcmascriptOptionsContext {
644                    esm_url_rewrite_behavior: url_rewrite_behavior,
645                    ..module_options_context.ecmascript
646                },
647                static_url_tag,
648                ..module_options_context
649            };
650
651            let foreign_code_module_options_context = ModuleOptionsContext {
652                module_rules: foreign_next_server_rules.clone(),
653                enable_webpack_loaders: foreign_enable_webpack_loaders,
654                // NOTE(WEB-1016) PostCSS transforms should also apply to foreign code.
655                enable_postcss_transform: enable_foreign_postcss_transform,
656                follow_reexports: true,
657                module_fragments_enabled: module_fragments_enabled_for_foreign_code,
658                ..module_options_context.clone()
659            };
660
661            let internal_module_options_context = ModuleOptionsContext {
662                ecmascript: EcmascriptOptionsContext {
663                    enable_typescript_transform: Some(
664                        TypescriptTransformOptions::default().resolved_cell(),
665                    ),
666                    enable_jsx: Some(JsxTransformOptions::default().resolved_cell()),
667                    ..module_options_context.ecmascript.clone()
668                },
669                module_rules: foreign_next_server_rules,
670                ..module_options_context.clone()
671            };
672
673            ModuleOptionsContext {
674                ecmascript: EcmascriptOptionsContext {
675                    enable_jsx: Some(jsx_runtime_options),
676                    enable_typescript_transform: Some(tsconfig),
677                    enable_decorators: Some(decorators_options.to_resolved().await?),
678                    enable_rust_react_compiler: None,
679                    ..module_options_context.ecmascript
680                },
681                enable_webpack_loaders,
682                enable_postcss_transform,
683                enable_mdx_rs,
684                rules: vec![
685                    (
686                        foreign_code_context_condition,
687                        foreign_code_module_options_context.resolved_cell(),
688                    ),
689                    (
690                        internal_assets_conditions().await?,
691                        internal_module_options_context.resolved_cell(),
692                    ),
693                ],
694                module_rules: next_server_rules,
695                ..module_options_context
696            }
697        }
698        ServerContextType::AppSSR { app_dir, .. } => {
699            foreign_next_server_rules.extend(internal_custom_rules);
700
701            next_server_rules.extend(source_transform_rules);
702            next_server_rules.push(
703                get_next_react_server_components_transform_rule(next_config, false, Some(app_dir))
704                    .await?,
705            );
706            next_server_rules.extend(page_transform_rules.clone());
707
708            let module_options_context = ModuleOptionsContext {
709                ecmascript: EcmascriptOptionsContext {
710                    esm_url_rewrite_behavior: Some(UrlRewriteBehavior::Relative),
711                    ..module_options_context.ecmascript
712                },
713                static_url_tag: Some(rcstr!("client")),
714                ..module_options_context
715            };
716
717            let foreign_code_module_options_context = ModuleOptionsContext {
718                module_rules: foreign_next_server_rules.clone(),
719                enable_webpack_loaders: foreign_enable_webpack_loaders,
720                // NOTE(WEB-1016) PostCSS transforms should also apply to foreign code.
721                enable_postcss_transform: enable_foreign_postcss_transform,
722                follow_reexports: true,
723                module_fragments_enabled: module_fragments_enabled_for_foreign_code,
724                ..module_options_context.clone()
725            };
726            let internal_module_options_context = ModuleOptionsContext {
727                ecmascript: EcmascriptOptionsContext {
728                    enable_typescript_transform: Some(
729                        TypescriptTransformOptions::default().resolved_cell(),
730                    ),
731                    ..module_options_context.ecmascript.clone()
732                },
733                module_rules: foreign_next_server_rules,
734                ..module_options_context.clone()
735            };
736
737            ModuleOptionsContext {
738                ecmascript: EcmascriptOptionsContext {
739                    enable_jsx: Some(jsx_runtime_options),
740                    enable_typescript_transform: Some(tsconfig),
741                    enable_decorators: Some(decorators_options.to_resolved().await?),
742                    enable_rust_react_compiler,
743                    rust_react_compiler_target,
744                    ..module_options_context.ecmascript
745                },
746                enable_webpack_loaders,
747                enable_postcss_transform,
748                enable_mdx_rs,
749                rules: vec![
750                    (
751                        foreign_code_context_condition,
752                        foreign_code_module_options_context.resolved_cell(),
753                    ),
754                    (
755                        internal_assets_conditions().await?,
756                        internal_module_options_context.resolved_cell(),
757                    ),
758                ],
759                module_rules: next_server_rules,
760                ..module_options_context
761            }
762        }
763        ServerContextType::AppRSC {
764            app_dir,
765            ecmascript_client_reference_transition_name,
766            ..
767        } => {
768            let client_directive_transformer =
769                if let Some(name) = ecmascript_client_reference_transition_name {
770                    Some(get_ecma_transform_rule(
771                        client_directive_transform_plugin(name)
772                            .to_resolved()
773                            .await?,
774                        enable_mdx_rs.is_some(),
775                        EcmascriptTransformStage::Preprocess,
776                    ))
777                } else {
778                    None
779                };
780
781            foreign_next_server_rules.extend(internal_custom_rules);
782            foreign_next_server_rules.extend(client_directive_transformer.clone());
783
784            next_server_rules.extend(source_transform_rules);
785            next_server_rules.push(
786                get_next_react_server_components_transform_rule(next_config, true, Some(app_dir))
787                    .await?,
788            );
789            next_server_rules.extend(client_directive_transformer.clone());
790            next_server_rules.extend(page_transform_rules);
791
792            let module_options_context = ModuleOptionsContext {
793                ecmascript: EcmascriptOptionsContext {
794                    esm_url_rewrite_behavior: Some(UrlRewriteBehavior::Relative),
795                    ..module_options_context.ecmascript
796                },
797                static_url_tag: Some(rcstr!("client")),
798                ..module_options_context
799            };
800
801            let foreign_code_module_options_context = ModuleOptionsContext {
802                module_rules: foreign_next_server_rules.clone(),
803                enable_webpack_loaders: foreign_enable_webpack_loaders,
804                // NOTE(WEB-1016) PostCSS transforms should also apply to foreign code.
805                enable_postcss_transform: enable_foreign_postcss_transform,
806                follow_reexports: true,
807                module_fragments_enabled: module_fragments_enabled_for_foreign_code,
808                ..module_options_context.clone()
809            };
810            let internal_module_options_context = ModuleOptionsContext {
811                ecmascript: EcmascriptOptionsContext {
812                    enable_typescript_transform: Some(
813                        TypescriptTransformOptions::default().resolved_cell(),
814                    ),
815                    ..module_options_context.ecmascript.clone()
816                },
817                module_rules: foreign_next_server_rules,
818                ..module_options_context.clone()
819            };
820            ModuleOptionsContext {
821                ecmascript: EcmascriptOptionsContext {
822                    enable_jsx: Some(rsc_jsx_runtime_options),
823                    enable_typescript_transform: Some(tsconfig),
824                    enable_decorators: Some(decorators_options.to_resolved().await?),
825                    enable_rust_react_compiler: None,
826                    ..module_options_context.ecmascript
827                },
828                enable_webpack_loaders,
829                enable_postcss_transform,
830                enable_mdx_rs,
831                rules: vec![
832                    (
833                        foreign_code_context_condition,
834                        foreign_code_module_options_context.resolved_cell(),
835                    ),
836                    (
837                        internal_assets_conditions().await?,
838                        internal_module_options_context.resolved_cell(),
839                    ),
840                ],
841                module_rules: next_server_rules,
842                ..module_options_context
843            }
844        }
845        ServerContextType::AppRoute {
846            app_dir,
847            ecmascript_client_reference_transition_name,
848        } => {
849            let mut common_next_server_rules = vec![
850                get_next_react_server_components_transform_rule(next_config, true, Some(app_dir))
851                    .await?,
852            ];
853
854            if let Some(ecmascript_client_reference_transition_name) =
855                ecmascript_client_reference_transition_name
856            {
857                common_next_server_rules.push(get_ecma_transform_rule(
858                    client_directive_transform_plugin(ecmascript_client_reference_transition_name)
859                        .to_resolved()
860                        .await?,
861                    enable_mdx_rs.is_some(),
862                    EcmascriptTransformStage::Preprocess,
863                ));
864            }
865
866            next_server_rules.extend(common_next_server_rules.iter().cloned());
867            internal_custom_rules.extend(common_next_server_rules);
868            foreign_next_server_rules.extend(internal_custom_rules.clone());
869
870            next_server_rules.extend(source_transform_rules);
871
872            let module_options_context = ModuleOptionsContext {
873                ecmascript: EcmascriptOptionsContext {
874                    esm_url_rewrite_behavior: Some(UrlRewriteBehavior::Full),
875                    ..module_options_context.ecmascript
876                },
877                ..module_options_context
878            };
879            let foreign_code_module_options_context = ModuleOptionsContext {
880                module_rules: foreign_next_server_rules.clone(),
881                enable_webpack_loaders: foreign_enable_webpack_loaders,
882                // NOTE(WEB-1016) PostCSS transforms should also apply to foreign code.
883                enable_postcss_transform: enable_foreign_postcss_transform,
884                follow_reexports: true,
885                module_fragments_enabled: module_fragments_enabled_for_foreign_code,
886                ..module_options_context.clone()
887            };
888            let internal_module_options_context = ModuleOptionsContext {
889                ecmascript: EcmascriptOptionsContext {
890                    enable_typescript_transform: Some(
891                        TypescriptTransformOptions::default().resolved_cell(),
892                    ),
893                    ..module_options_context.ecmascript.clone()
894                },
895                module_rules: internal_custom_rules,
896                ..module_options_context.clone()
897            };
898            ModuleOptionsContext {
899                ecmascript: EcmascriptOptionsContext {
900                    enable_jsx: Some(rsc_jsx_runtime_options),
901                    enable_typescript_transform: Some(tsconfig),
902                    enable_decorators: Some(decorators_options.to_resolved().await?),
903                    enable_rust_react_compiler: None,
904                    ..module_options_context.ecmascript
905                },
906                enable_webpack_loaders,
907                enable_postcss_transform,
908                enable_mdx_rs,
909                rules: vec![
910                    (
911                        foreign_code_context_condition,
912                        foreign_code_module_options_context.resolved_cell(),
913                    ),
914                    (
915                        internal_assets_conditions().await?,
916                        internal_module_options_context.resolved_cell(),
917                    ),
918                ],
919                module_rules: next_server_rules,
920                ..module_options_context
921            }
922        }
923        ServerContextType::Middleware {
924            app_dir,
925            ecmascript_client_reference_transition_name,
926        }
927        | ServerContextType::Instrumentation {
928            app_dir,
929            ecmascript_client_reference_transition_name,
930        } => {
931            let directive_transform_rule =
932                if let Some(name) = ecmascript_client_reference_transition_name {
933                    get_ecma_transform_rule(
934                        client_directive_transform_plugin(name)
935                            .to_resolved()
936                            .await?,
937                        enable_mdx_rs.is_some(),
938                        EcmascriptTransformStage::Preprocess,
939                    )
940                } else {
941                    get_ecma_transform_rule(
942                        client_disallowed_directive_transform_plugin(rcstr!(
943                            "next/dist/client/use-client-disallowed.js"
944                        ))
945                        .to_resolved()
946                        .await?,
947                        enable_mdx_rs.is_some(),
948                        EcmascriptTransformStage::Preprocess,
949                    )
950                };
951            let custom_source_transform_rules: Vec<ModuleRule> = vec![
952                directive_transform_rule,
953                get_next_react_server_components_transform_rule(next_config, true, app_dir).await?,
954            ];
955
956            internal_custom_rules.extend(custom_source_transform_rules.iter().cloned());
957
958            next_server_rules.extend(custom_source_transform_rules);
959            next_server_rules.extend(source_transform_rules);
960
961            let module_options_context = ModuleOptionsContext {
962                ecmascript: EcmascriptOptionsContext {
963                    esm_url_rewrite_behavior: Some(UrlRewriteBehavior::Full),
964                    ..module_options_context.ecmascript
965                },
966                ..module_options_context
967            };
968            let foreign_code_module_options_context = ModuleOptionsContext {
969                module_rules: internal_custom_rules.clone(),
970                enable_webpack_loaders: foreign_enable_webpack_loaders,
971                // NOTE(WEB-1016) PostCSS transforms should also apply to foreign code.
972                enable_postcss_transform: enable_foreign_postcss_transform,
973                follow_reexports: true,
974                module_fragments_enabled: module_fragments_enabled_for_foreign_code,
975                ..module_options_context.clone()
976            };
977            let internal_module_options_context = ModuleOptionsContext {
978                ecmascript: EcmascriptOptionsContext {
979                    enable_typescript_transform: Some(
980                        TypescriptTransformOptions::default().resolved_cell(),
981                    ),
982                    ..module_options_context.ecmascript.clone()
983                },
984                module_rules: internal_custom_rules,
985                ..module_options_context.clone()
986            };
987            ModuleOptionsContext {
988                ecmascript: EcmascriptOptionsContext {
989                    enable_jsx: Some(jsx_runtime_options),
990                    enable_typescript_transform: Some(tsconfig),
991                    enable_decorators: Some(decorators_options.to_resolved().await?),
992                    enable_rust_react_compiler: None,
993                    ..module_options_context.ecmascript
994                },
995                enable_webpack_loaders,
996                enable_postcss_transform,
997                enable_mdx_rs,
998                rules: vec![
999                    (
1000                        foreign_code_context_condition,
1001                        foreign_code_module_options_context.resolved_cell(),
1002                    ),
1003                    (
1004                        internal_assets_conditions().await?,
1005                        internal_module_options_context.resolved_cell(),
1006                    ),
1007                ],
1008                module_rules: next_server_rules,
1009                ..module_options_context
1010            }
1011        }
1012    }
1013    .cell();
1014
1015    Ok(module_options_context)
1016}
1017
1018#[turbo_tasks::function]
1019fn client_directive_transform_plugin(transition_name: RcStr) -> Vc<TransformPlugin> {
1020    Vc::cell(Box::new(ClientDirectiveTransformer::new(transition_name))
1021        as Box<dyn CustomTransformer + Send + Sync>)
1022}
1023
1024#[turbo_tasks::function]
1025fn client_disallowed_directive_transform_plugin(error_proxy_module: RcStr) -> Vc<TransformPlugin> {
1026    Vc::cell(Box::new(ClientDisallowedDirectiveTransformer::new(
1027        error_proxy_module.to_string(),
1028    )) as Box<dyn CustomTransformer + Send + Sync>)
1029}
1030
1031#[turbo_tasks::task_input(contains_unresolved_vcs)]
1032#[derive(Clone, Debug, PartialEq, Eq, Hash, TraceRawVcs, Encode, Decode)]
1033pub struct ServerChunkingContextOptions {
1034    pub mode: Vc<NextMode>,
1035    pub root_path: FileSystemPath,
1036    pub node_root: FileSystemPath,
1037    pub node_root_to_root_path: RcStr,
1038    pub environment: Vc<Environment>,
1039    pub module_id_strategy: Vc<ModuleIdStrategy>,
1040    pub export_usage: Vc<OptionBindingUsageInfo>,
1041    pub unused_references: Vc<UnusedReferences>,
1042    pub minify: Vc<bool>,
1043    pub source_maps: Vc<SourceMapsType>,
1044    pub no_mangling: Vc<bool>,
1045    pub scope_hoisting: Vc<bool>,
1046    pub nested_async_chunking: Vc<bool>,
1047    pub debug_ids: Vc<bool>,
1048    pub client_root: FileSystemPath,
1049    pub client_static_folder_name: RcStr,
1050    pub asset_prefix: RcStr,
1051    pub css_url_suffix: Vc<Option<RcStr>>,
1052    pub hash_salt: ResolvedVc<RcStr>,
1053    pub style_groups_algorithm: StyleGroupsAlgorithm,
1054}
1055
1056/// Like `get_server_chunking_context` but all assets are emitted as client assets (so `/_next`)
1057#[turbo_tasks::function]
1058pub async fn get_server_chunking_context_with_client_assets(
1059    options: ServerChunkingContextOptions,
1060) -> Result<Vc<NodeJsChunkingContext>> {
1061    let ServerChunkingContextOptions {
1062        mode,
1063        root_path,
1064        node_root,
1065        node_root_to_root_path,
1066        environment,
1067        module_id_strategy,
1068        export_usage,
1069        unused_references,
1070        minify,
1071        source_maps,
1072        no_mangling,
1073        scope_hoisting,
1074        nested_async_chunking,
1075        debug_ids,
1076        client_root,
1077        client_static_folder_name,
1078        asset_prefix,
1079        css_url_suffix,
1080        hash_salt,
1081        style_groups_algorithm,
1082    } = options;
1083    let css_url_suffix = css_url_suffix.to_resolved().await?;
1084
1085    let next_mode = mode.await?;
1086    // TODO(alexkirsz) This should return a trait that can be implemented by the
1087    // different server chunking contexts. OR the build chunking context should
1088    // support both production and development modes.
1089    let mut builder = NodeJsChunkingContext::builder(
1090        root_path,
1091        node_root.clone(),
1092        node_root_to_root_path,
1093        client_root.clone(),
1094        node_root.join("server/chunks/ssr")?,
1095        client_root
1096            .join(&client_static_folder_name)?
1097            .join("media")?,
1098        environment.to_resolved().await?,
1099        next_mode.runtime_type(),
1100    )
1101    .asset_prefix(Some(asset_prefix))
1102    .url_behavior_override(
1103        rcstr!("client"),
1104        UrlBehavior {
1105            suffix: AssetSuffix::FromGlobal(rcstr!("NEXT_CLIENT_ASSET_SUFFIX")),
1106            static_suffix: css_url_suffix,
1107        },
1108    )
1109    .default_url_behavior(UrlBehavior {
1110        suffix: AssetSuffix::Inferred,
1111        static_suffix: ResolvedVc::cell(None),
1112    })
1113    .minify_type(if *minify.await? {
1114        MinifyType::Minify {
1115            // React needs deterministic function names to work correctly.
1116            mangle: (!*no_mangling.await?).then_some(MangleType::Deterministic),
1117        }
1118    } else {
1119        MinifyType::NoMinify
1120    })
1121    .source_maps(*source_maps.await?)
1122    .module_id_strategy(module_id_strategy.to_resolved().await?)
1123    .export_usage(*export_usage.await?)
1124    .unused_references(unused_references.to_resolved().await?)
1125    .debug_ids(*debug_ids.await?)
1126    .hash_salt(hash_salt)
1127    .nested_async_availability(*nested_async_chunking.await?)
1128    .worker_forwarded_globals(worker_forwarded_globals());
1129
1130    builder = builder.source_map_source_type(if next_mode.is_development() {
1131        SourceMapSourceType::AbsoluteFileUri
1132    } else {
1133        SourceMapSourceType::RelativeUri
1134    });
1135    if next_mode.is_production() {
1136        builder = builder
1137            .chunking_config(
1138                Vc::<EcmascriptChunkType>::default().to_resolved().await?,
1139                ChunkingConfig {
1140                    min_chunk_size: 20_000,
1141                    max_chunk_count_per_group: 100,
1142                    max_merge_chunk_size: 100_000,
1143                    ..Default::default()
1144                },
1145            )
1146            .chunking_config(
1147                Vc::<CssChunkType>::default().to_resolved().await?,
1148                ChunkingConfig {
1149                    max_merge_chunk_size: 100_000,
1150                    style_groups_algorithm: style_groups_algorithm.clone(),
1151                    ..Default::default()
1152                },
1153            )
1154            .module_merging(*scope_hoisting.await?);
1155    }
1156
1157    Ok(builder.build())
1158}
1159
1160// By default, assets are server assets, but the StructuredImageModuleType ones are on the client
1161#[turbo_tasks::function]
1162pub async fn get_server_chunking_context(
1163    options: ServerChunkingContextOptions,
1164) -> Result<Vc<NodeJsChunkingContext>> {
1165    let ServerChunkingContextOptions {
1166        mode,
1167        root_path,
1168        node_root,
1169        node_root_to_root_path,
1170        environment,
1171        module_id_strategy,
1172        export_usage,
1173        unused_references,
1174        minify,
1175        source_maps,
1176        no_mangling,
1177        scope_hoisting,
1178        nested_async_chunking,
1179        debug_ids,
1180        client_root,
1181        client_static_folder_name,
1182        asset_prefix,
1183        css_url_suffix,
1184        hash_salt,
1185        style_groups_algorithm,
1186    } = options;
1187    let css_url_suffix = css_url_suffix.to_resolved().await?;
1188    let next_mode = mode.await?;
1189    // TODO(alexkirsz) This should return a trait that can be implemented by the
1190    // different server chunking contexts. OR the build chunking context should
1191    // support both production and development modes.
1192    let mut builder = NodeJsChunkingContext::builder(
1193        root_path,
1194        node_root.clone(),
1195        node_root_to_root_path,
1196        node_root.clone(),
1197        node_root.join("server/chunks")?,
1198        node_root.join("server/assets")?,
1199        environment.to_resolved().await?,
1200        next_mode.runtime_type(),
1201    )
1202    .client_roots_override(rcstr!("client"), client_root.clone())
1203    .asset_root_path_override(
1204        rcstr!("client"),
1205        client_root
1206            .join(&client_static_folder_name)?
1207            .join("media")?,
1208    )
1209    .asset_prefix_override(rcstr!("client"), asset_prefix)
1210    .url_behavior_override(
1211        rcstr!("client"),
1212        UrlBehavior {
1213            suffix: AssetSuffix::FromGlobal(rcstr!("NEXT_CLIENT_ASSET_SUFFIX")),
1214            static_suffix: css_url_suffix,
1215        },
1216    )
1217    .default_url_behavior(UrlBehavior {
1218        suffix: AssetSuffix::Inferred,
1219        static_suffix: ResolvedVc::cell(None),
1220    })
1221    .minify_type(if *minify.await? {
1222        MinifyType::Minify {
1223            mangle: (!*no_mangling.await?).then_some(MangleType::OptimalSize),
1224        }
1225    } else {
1226        MinifyType::NoMinify
1227    })
1228    .source_maps(*source_maps.await?)
1229    .module_id_strategy(module_id_strategy.to_resolved().await?)
1230    .export_usage(*export_usage.await?)
1231    .unused_references(unused_references.to_resolved().await?)
1232    .debug_ids(*debug_ids.await?)
1233    .hash_salt(hash_salt)
1234    .nested_async_availability(*nested_async_chunking.await?)
1235    .worker_forwarded_globals(worker_forwarded_globals());
1236
1237    if next_mode.is_development() {
1238        builder = builder.source_map_source_type(SourceMapSourceType::AbsoluteFileUri);
1239    } else {
1240        builder = builder
1241            .source_map_source_type(SourceMapSourceType::RelativeUri)
1242            .chunking_config(
1243                Vc::<EcmascriptChunkType>::default().to_resolved().await?,
1244                ChunkingConfig {
1245                    min_chunk_size: 20_000,
1246                    max_chunk_count_per_group: 100,
1247                    max_merge_chunk_size: 100_000,
1248                    ..Default::default()
1249                },
1250            )
1251            .chunking_config(
1252                Vc::<CssChunkType>::default().to_resolved().await?,
1253                ChunkingConfig {
1254                    max_merge_chunk_size: 100_000,
1255                    style_groups_algorithm: style_groups_algorithm.clone(),
1256                    ..Default::default()
1257                },
1258            )
1259            .module_merging(*scope_hoisting.await?);
1260    }
1261
1262    Ok(builder.build())
1263}