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