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