next_core/next_server/
context.rs

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