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