Skip to main content

next_core/next_server/
context.rs

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