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