Skip to main content

next_core/next_server/
context.rs

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