next_core/
next_import_map.rs

1use std::{collections::BTreeMap, sync::LazyLock};
2
3use anyhow::{Context, Result};
4use either::Either;
5use rustc_hash::FxHashMap;
6use turbo_rcstr::{RcStr, rcstr};
7use turbo_tasks::{FxIndexMap, ResolvedVc, Vc, fxindexmap};
8use turbo_tasks_fs::{FileSystem, FileSystemPath};
9use turbopack_core::{
10    reference_type::{CommonJsReferenceSubType, ReferenceType},
11    resolve::{
12        AliasPattern, ExternalTraced, ExternalType, ResolveAliasMap, SubpathValue,
13        node::node_cjs_resolve_options,
14        options::{ConditionValue, ImportMap, ImportMapping, ResolvedMap},
15        parse::Request,
16        pattern::Pattern,
17        resolve,
18    },
19    source::Source,
20};
21use turbopack_node::execution_context::ExecutionContext;
22
23use crate::{
24    app_structure::CollectedRootParams,
25    embed_js::{VIRTUAL_PACKAGE_NAME, next_js_fs},
26    mode::NextMode,
27    next_client::context::ClientContextType,
28    next_config::NextConfig,
29    next_edge::unsupported::NextEdgeUnsupportedModuleReplacer,
30    next_font::google::{
31        GOOGLE_FONTS_INTERNAL_PREFIX, NextFontGoogleCssModuleReplacer,
32        NextFontGoogleFontFileReplacer, NextFontGoogleReplacer,
33    },
34    next_root_params::insert_next_root_params_mapping,
35    next_server::context::ServerContextType,
36    util::NextRuntime,
37};
38
39/// List of node.js internals that are not supported by edge runtime.
40/// If these imports are used & user does not provide alias for the polyfill,
41/// runtime error will be thrown.
42/// This is not identical to the list of entire node.js internals, refer
43/// https://vercel.com/docs/functions/runtimes/edge-runtime#compatible-node.js-modules
44/// for the allowed imports.
45static EDGE_UNSUPPORTED_NODE_INTERNALS: LazyLock<[RcStr; 44]> = LazyLock::new(|| {
46    [
47        rcstr!("child_process"),
48        rcstr!("cluster"),
49        rcstr!("console"),
50        rcstr!("constants"),
51        rcstr!("crypto"),
52        rcstr!("dgram"),
53        rcstr!("diagnostics_channel"),
54        rcstr!("dns"),
55        rcstr!("dns/promises"),
56        rcstr!("domain"),
57        rcstr!("fs"),
58        rcstr!("fs/promises"),
59        rcstr!("http"),
60        rcstr!("http2"),
61        rcstr!("https"),
62        rcstr!("inspector"),
63        rcstr!("module"),
64        rcstr!("net"),
65        rcstr!("os"),
66        rcstr!("path"),
67        rcstr!("path/posix"),
68        rcstr!("path/win32"),
69        rcstr!("perf_hooks"),
70        rcstr!("process"),
71        rcstr!("punycode"),
72        rcstr!("querystring"),
73        rcstr!("readline"),
74        rcstr!("repl"),
75        rcstr!("stream"),
76        rcstr!("stream/promises"),
77        rcstr!("stream/web"),
78        rcstr!("string_decoder"),
79        rcstr!("sys"),
80        rcstr!("timers"),
81        rcstr!("timers/promises"),
82        rcstr!("tls"),
83        rcstr!("trace_events"),
84        rcstr!("tty"),
85        rcstr!("v8"),
86        rcstr!("vm"),
87        rcstr!("wasi"),
88        rcstr!("worker_threads"),
89        rcstr!("zlib"),
90        rcstr!("pnpapi"),
91    ]
92});
93
94// Make sure to not add any external requests here.
95/// Computes the Next-specific client import map.
96#[turbo_tasks::function]
97pub async fn get_next_client_import_map(
98    project_path: FileSystemPath,
99    ty: ClientContextType,
100    next_config: Vc<NextConfig>,
101    next_mode: Vc<NextMode>,
102    execution_context: Vc<ExecutionContext>,
103) -> Result<Vc<ImportMap>> {
104    let mut import_map = ImportMap::empty();
105
106    insert_next_shared_aliases(
107        &mut import_map,
108        project_path.clone(),
109        execution_context,
110        next_config,
111        next_mode,
112        false,
113    )
114    .await?;
115
116    insert_optimized_module_aliases(&mut import_map, project_path.clone()).await?;
117
118    insert_alias_option(
119        &mut import_map,
120        &project_path,
121        next_config.resolve_alias_options(),
122        ["browser"],
123    )
124    .await?;
125
126    match &ty {
127        ClientContextType::Pages { .. } => {}
128        ClientContextType::App { app_dir } => {
129            let react_flavor = if *next_config.enable_ppr().await?
130                || *next_config.enable_taint().await?
131                || *next_config.enable_view_transition().await?
132                || *next_config.enable_router_bfcache().await?
133            {
134                "-experimental"
135            } else {
136                ""
137            };
138
139            import_map.insert_exact_alias(
140                rcstr!("react"),
141                request_to_import_mapping(
142                    app_dir.clone(),
143                    format!("next/dist/compiled/react{react_flavor}").into(),
144                ),
145            );
146            import_map.insert_wildcard_alias(
147                rcstr!("react/"),
148                request_to_import_mapping(
149                    app_dir.clone(),
150                    format!("next/dist/compiled/react{react_flavor}/*").into(),
151                ),
152            );
153            import_map.insert_exact_alias(
154                rcstr!("react-dom"),
155                request_to_import_mapping(
156                    app_dir.clone(),
157                    format!("next/dist/compiled/react-dom{react_flavor}").into(),
158                ),
159            );
160            import_map.insert_exact_alias(
161                rcstr!("react-dom/static"),
162                request_to_import_mapping(
163                    app_dir.clone(),
164                    rcstr!("next/dist/compiled/react-dom-experimental/static"),
165                ),
166            );
167            import_map.insert_exact_alias(
168                rcstr!("react-dom/static.edge"),
169                request_to_import_mapping(
170                    app_dir.clone(),
171                    rcstr!("next/dist/compiled/react-dom-experimental/static.edge"),
172                ),
173            );
174            import_map.insert_exact_alias(
175                rcstr!("react-dom/static.browser"),
176                request_to_import_mapping(
177                    app_dir.clone(),
178                    rcstr!("next/dist/compiled/react-dom-experimental/static.browser"),
179                ),
180            );
181            let react_client_package = get_react_client_package(next_config).await?;
182            import_map.insert_exact_alias(
183                rcstr!("react-dom/client"),
184                request_to_import_mapping(
185                    app_dir.clone(),
186                    format!("next/dist/compiled/react-dom{react_flavor}/{react_client_package}")
187                        .into(),
188                ),
189            );
190            import_map.insert_wildcard_alias(
191                rcstr!("react-dom/"),
192                request_to_import_mapping(
193                    app_dir.clone(),
194                    format!("next/dist/compiled/react-dom{react_flavor}/*").into(),
195                ),
196            );
197            import_map.insert_wildcard_alias(
198                rcstr!("react-server-dom-webpack/"),
199                request_to_import_mapping(app_dir.clone(), rcstr!("react-server-dom-turbopack/*")),
200            );
201            import_map.insert_wildcard_alias(
202                rcstr!("react-server-dom-turbopack/"),
203                request_to_import_mapping(
204                    app_dir.clone(),
205                    format!("next/dist/compiled/react-server-dom-turbopack{react_flavor}/*").into(),
206                ),
207            );
208            insert_exact_alias_or_js(
209                &mut import_map,
210                rcstr!("next/head"),
211                request_to_import_mapping(
212                    project_path.clone(),
213                    rcstr!("next/dist/client/components/noop-head"),
214                ),
215            );
216            insert_exact_alias_or_js(
217                &mut import_map,
218                rcstr!("next/dynamic"),
219                request_to_import_mapping(
220                    project_path.clone(),
221                    rcstr!("next/dist/shared/lib/app-dynamic"),
222                ),
223            );
224            insert_exact_alias_or_js(
225                &mut import_map,
226                rcstr!("next/link"),
227                request_to_import_mapping(
228                    project_path.clone(),
229                    rcstr!("next/dist/client/app-dir/link"),
230                ),
231            );
232            insert_exact_alias_or_js(
233                &mut import_map,
234                rcstr!("next/form"),
235                request_to_import_mapping(
236                    project_path.clone(),
237                    rcstr!("next/dist/client/app-dir/form"),
238                ),
239            );
240        }
241        ClientContextType::Fallback => {}
242        ClientContextType::Other => {}
243    }
244
245    // see https://github.com/vercel/next.js/blob/8013ef7372fc545d49dbd060461224ceb563b454/packages/next/src/build/webpack-config.ts#L1449-L1531
246    insert_exact_alias_map(
247        &mut import_map,
248        project_path.clone(),
249        fxindexmap! {
250            rcstr!("server-only") => rcstr!("next/dist/compiled/server-only/index"),
251            rcstr!("client-only") => rcstr!("next/dist/compiled/client-only/index"),
252            rcstr!("next/dist/compiled/server-only") => rcstr!("next/dist/compiled/server-only/index"),
253            rcstr!("next/dist/compiled/client-only") => rcstr!("next/dist/compiled/client-only/index"),
254        },
255    );
256    insert_next_root_params_mapping(
257        &mut import_map,
258        next_config.enable_root_params(),
259        Either::Right(ty.clone()),
260        None,
261    )
262    .await?;
263
264    match ty {
265        ClientContextType::Pages { .. }
266        | ClientContextType::App { .. }
267        | ClientContextType::Fallback => {
268            for (original, alias) in NEXT_ALIASES.iter() {
269                import_map.insert_exact_alias(
270                    format!("node:{original}"),
271                    request_to_import_mapping(project_path.clone(), alias.clone()),
272                );
273            }
274        }
275        ClientContextType::Other => {}
276    }
277
278    insert_turbopack_dev_alias(&mut import_map).await?;
279    insert_instrumentation_client_alias(&mut import_map, project_path).await?;
280
281    Ok(import_map.cell())
282}
283
284/// Computes the Next-specific client fallback import map, which provides
285/// polyfills to Node.js externals.
286#[turbo_tasks::function]
287pub async fn get_next_client_fallback_import_map(ty: ClientContextType) -> Result<Vc<ImportMap>> {
288    let mut import_map = ImportMap::empty();
289
290    match ty {
291        ClientContextType::Pages {
292            pages_dir: context_dir,
293        }
294        | ClientContextType::App {
295            app_dir: context_dir,
296        } => {
297            for (original, alias) in NEXT_ALIASES.iter() {
298                import_map.insert_exact_alias(
299                    original.clone(),
300                    request_to_import_mapping(context_dir.clone(), alias.clone()),
301                );
302            }
303        }
304        ClientContextType::Fallback => {}
305        ClientContextType::Other => {}
306    }
307
308    insert_turbopack_dev_alias(&mut import_map).await?;
309
310    Ok(import_map.cell())
311}
312
313/// Computes the Next-specific server-side import map.
314#[turbo_tasks::function]
315pub async fn get_next_server_import_map(
316    project_path: FileSystemPath,
317    ty: ServerContextType,
318    next_config: Vc<NextConfig>,
319    next_mode: Vc<NextMode>,
320    execution_context: Vc<ExecutionContext>,
321    collected_root_params: Option<Vc<CollectedRootParams>>,
322) -> Result<Vc<ImportMap>> {
323    let mut import_map = ImportMap::empty();
324
325    insert_next_shared_aliases(
326        &mut import_map,
327        project_path.clone(),
328        execution_context,
329        next_config,
330        next_mode,
331        false,
332    )
333    .await?;
334
335    insert_alias_option(
336        &mut import_map,
337        &project_path,
338        next_config.resolve_alias_options(),
339        [],
340    )
341    .await?;
342
343    let external = ImportMapping::External(None, ExternalType::CommonJs, ExternalTraced::Traced)
344        .resolved_cell();
345
346    import_map.insert_exact_alias(rcstr!("next/dist/server/require-hook"), external);
347    match ty {
348        ServerContextType::Pages { .. }
349        | ServerContextType::PagesData { .. }
350        | ServerContextType::PagesApi { .. } => {
351            import_map.insert_exact_alias(rcstr!("react"), external);
352            import_map.insert_wildcard_alias(rcstr!("react/"), external);
353            import_map.insert_exact_alias(rcstr!("react-dom"), external);
354            import_map.insert_exact_alias(rcstr!("react-dom/client"), external);
355            import_map.insert_wildcard_alias(rcstr!("react-dom/"), external);
356            import_map.insert_exact_alias(rcstr!("styled-jsx"), external);
357            import_map.insert_exact_alias(
358                rcstr!("styled-jsx/style"),
359                ImportMapping::External(
360                    Some(rcstr!("styled-jsx/style.js")),
361                    ExternalType::CommonJs,
362                    ExternalTraced::Traced,
363                )
364                .resolved_cell(),
365            );
366            import_map.insert_wildcard_alias(rcstr!("styled-jsx/"), external);
367            // TODO: we should not bundle next/dist/build/utils in the pages renderer at all
368            import_map.insert_wildcard_alias(rcstr!("next/dist/build/utils"), external);
369        }
370        ServerContextType::AppSSR { .. }
371        | ServerContextType::AppRSC { .. }
372        | ServerContextType::AppRoute { .. } => {
373            insert_exact_alias_or_js(
374                &mut import_map,
375                rcstr!("next/head"),
376                request_to_import_mapping(
377                    project_path.clone(),
378                    rcstr!("next/dist/client/components/noop-head"),
379                ),
380            );
381            insert_exact_alias_or_js(
382                &mut import_map,
383                rcstr!("next/dynamic"),
384                request_to_import_mapping(
385                    project_path.clone(),
386                    rcstr!("next/dist/shared/lib/app-dynamic"),
387                ),
388            );
389            insert_exact_alias_or_js(
390                &mut import_map,
391                rcstr!("next/link"),
392                request_to_import_mapping(
393                    project_path.clone(),
394                    rcstr!("next/dist/client/app-dir/link"),
395                ),
396            );
397            insert_exact_alias_or_js(
398                &mut import_map,
399                rcstr!("next/form"),
400                request_to_import_mapping(
401                    project_path.clone(),
402                    rcstr!("next/dist/client/app-dir/form"),
403                ),
404            );
405        }
406        ServerContextType::Middleware { .. } | ServerContextType::Instrumentation { .. } => {}
407    }
408
409    insert_next_server_special_aliases(
410        &mut import_map,
411        project_path.clone(),
412        ty,
413        NextRuntime::NodeJs,
414        next_config,
415        collected_root_params,
416    )
417    .await?;
418
419    Ok(import_map.cell())
420}
421
422/// Computes the Next-specific edge-side import map.
423#[turbo_tasks::function]
424pub async fn get_next_edge_import_map(
425    project_path: FileSystemPath,
426    ty: ServerContextType,
427    next_config: Vc<NextConfig>,
428    next_mode: Vc<NextMode>,
429    execution_context: Vc<ExecutionContext>,
430    collected_root_params: Option<Vc<CollectedRootParams>>,
431) -> Result<Vc<ImportMap>> {
432    let mut import_map = ImportMap::empty();
433
434    // https://github.com/vercel/next.js/blob/786ef25e529e1fb2dda398aebd02ccbc8d0fb673/packages/next/src/build/webpack-config.ts#L815-L861
435
436    // Alias next/dist imports to next/dist/esm assets
437    insert_wildcard_alias_map(
438        &mut import_map,
439        project_path.clone(),
440        fxindexmap! {
441            rcstr!("next/dist/build/") => rcstr!("next/dist/esm/build/*"),
442            rcstr!("next/dist/client/") => rcstr!("next/dist/esm/client/*"),
443            rcstr!("next/dist/shared/") => rcstr!("next/dist/esm/shared/*"),
444            rcstr!("next/dist/pages/") => rcstr!("next/dist/esm/pages/*"),
445            rcstr!("next/dist/lib/") => rcstr!("next/dist/esm/lib/*"),
446            rcstr!("next/dist/server/") => rcstr!("next/dist/esm/server/*"),
447            rcstr!("next/dist/api/") => rcstr!("next/dist/esm/api/*"),
448        },
449    );
450
451    // Alias the usage of next public APIs
452    insert_exact_alias_map(
453        &mut import_map,
454        project_path.clone(),
455        fxindexmap! {
456            rcstr!("next/app") => rcstr!("next/dist/api/app"),
457            rcstr!("next/document") => rcstr!("next/dist/api/document"),
458            rcstr!("next/dynamic") => rcstr!("next/dist/api/dynamic"),
459            rcstr!("next/form") => rcstr!("next/dist/api/form"),
460            rcstr!("next/head") => rcstr!("next/dist/api/head"),
461            rcstr!("next/headers") => rcstr!("next/dist/api/headers"),
462            rcstr!("next/image") => rcstr!("next/dist/api/image"),
463            rcstr!("next/link") => rcstr!("next/dist/api/link"),
464            rcstr!("next/navigation") => rcstr!("next/dist/api/navigation"),
465            rcstr!("next/router") => rcstr!("next/dist/api/router"),
466            rcstr!("next/script") => rcstr!("next/dist/api/script"),
467            rcstr!("next/server") => rcstr!("next/dist/api/server"),
468            rcstr!("next/og") => rcstr!("next/dist/api/og"),
469
470            // Alias built-in @vercel/og to edge bundle for edge runtime
471            rcstr!("next/dist/compiled/@vercel/og/index.node.js") => rcstr!("next/dist/compiled/@vercel/og/index.edge.js"),
472        },
473    );
474
475    insert_next_shared_aliases(
476        &mut import_map,
477        project_path.clone(),
478        execution_context,
479        next_config,
480        next_mode,
481        true,
482    )
483    .await?;
484
485    insert_optimized_module_aliases(&mut import_map, project_path.clone()).await?;
486
487    insert_alias_option(
488        &mut import_map,
489        &project_path,
490        next_config.resolve_alias_options(),
491        [],
492    )
493    .await?;
494
495    match &ty {
496        ServerContextType::Pages { .. }
497        | ServerContextType::PagesData { .. }
498        | ServerContextType::PagesApi { .. }
499        | ServerContextType::Middleware { .. }
500        | ServerContextType::Instrumentation { .. } => {}
501        ServerContextType::AppSSR { .. }
502        | ServerContextType::AppRSC { .. }
503        | ServerContextType::AppRoute { .. } => {
504            insert_exact_alias_or_js(
505                &mut import_map,
506                rcstr!("next/head"),
507                request_to_import_mapping(
508                    project_path.clone(),
509                    rcstr!("next/dist/client/components/noop-head"),
510                ),
511            );
512            insert_exact_alias_or_js(
513                &mut import_map,
514                rcstr!("next/dynamic"),
515                request_to_import_mapping(
516                    project_path.clone(),
517                    rcstr!("next/dist/shared/lib/app-dynamic"),
518                ),
519            );
520            insert_exact_alias_or_js(
521                &mut import_map,
522                rcstr!("next/link"),
523                request_to_import_mapping(
524                    project_path.clone(),
525                    rcstr!("next/dist/client/app-dir/link"),
526                ),
527            );
528        }
529    }
530
531    insert_next_server_special_aliases(
532        &mut import_map,
533        project_path.clone(),
534        ty.clone(),
535        NextRuntime::Edge,
536        next_config,
537        collected_root_params,
538    )
539    .await?;
540
541    // Look for where 'server/web/globals.ts` are imported to find out corresponding
542    // context
543    match ty {
544        ServerContextType::AppSSR { .. }
545        | ServerContextType::AppRSC { .. }
546        | ServerContextType::AppRoute { .. }
547        | ServerContextType::Middleware { .. }
548        | ServerContextType::Instrumentation { .. }
549        | ServerContextType::Pages { .. }
550        | ServerContextType::PagesData { .. }
551        | ServerContextType::PagesApi { .. } => {
552            insert_unsupported_node_internal_aliases(&mut import_map).await?;
553        }
554    }
555
556    Ok(import_map.cell())
557}
558
559/// Computes the Next-specific server-side and edge-side fallback import map.
560#[turbo_tasks::function]
561pub async fn get_next_edge_and_server_fallback_import_map(
562    project_path: FileSystemPath,
563    runtime: NextRuntime,
564) -> Result<Vc<ImportMap>> {
565    let mut fallback_import_map = ImportMap::empty();
566
567    let external_cjs_if_node = move |context_dir: FileSystemPath, request: RcStr| match runtime {
568        NextRuntime::Edge => request_to_import_mapping(context_dir, request),
569        NextRuntime::NodeJs => external_request_to_cjs_import_mapping(context_dir, request),
570    };
571
572    fallback_import_map.insert_exact_alias(
573        rcstr!("@opentelemetry/api"),
574        // It needs to prefer the local version of @opentelemetry/api, so put this in the fallback
575        // import map
576        ImportMapping::Alternatives(vec![external_cjs_if_node(
577            project_path,
578            rcstr!("next/dist/compiled/@opentelemetry/api"),
579        )])
580        .resolved_cell(),
581    );
582    Ok(fallback_import_map.cell())
583}
584
585/// Insert default aliases for the node.js's internal to raise unsupported
586/// runtime errors. User may provide polyfills for their own by setting user
587/// config's alias.
588async fn insert_unsupported_node_internal_aliases(import_map: &mut ImportMap) -> Result<()> {
589    let unsupported_replacer = ImportMapping::Dynamic(ResolvedVc::upcast(
590        NextEdgeUnsupportedModuleReplacer::new()
591            .to_resolved()
592            .await?,
593    ))
594    .resolved_cell();
595
596    EDGE_UNSUPPORTED_NODE_INTERNALS.iter().for_each(|module| {
597        import_map.insert_alias(AliasPattern::exact(module.clone()), unsupported_replacer);
598    });
599    Ok(())
600}
601
602pub fn get_next_client_resolved_map(
603    _context: FileSystemPath,
604    _root: FileSystemPath,
605    _mode: NextMode,
606) -> Vc<ResolvedMap> {
607    let glob_mappings = vec![];
608    ResolvedMap {
609        by_glob: glob_mappings,
610    }
611    .cell()
612}
613
614static NEXT_ALIASES: LazyLock<[(RcStr, RcStr); 23]> = LazyLock::new(|| {
615    [
616        (rcstr!("assert"), rcstr!("next/dist/compiled/assert")),
617        (rcstr!("buffer"), rcstr!("next/dist/compiled/buffer")),
618        (
619            rcstr!("constants"),
620            rcstr!("next/dist/compiled/constants-browserify"),
621        ),
622        (
623            rcstr!("crypto"),
624            rcstr!("next/dist/compiled/crypto-browserify"),
625        ),
626        (
627            rcstr!("domain"),
628            rcstr!("next/dist/compiled/domain-browser"),
629        ),
630        (rcstr!("http"), rcstr!("next/dist/compiled/stream-http")),
631        (
632            rcstr!("https"),
633            rcstr!("next/dist/compiled/https-browserify"),
634        ),
635        (rcstr!("os"), rcstr!("next/dist/compiled/os-browserify")),
636        (rcstr!("path"), rcstr!("next/dist/compiled/path-browserify")),
637        (rcstr!("punycode"), rcstr!("next/dist/compiled/punycode")),
638        (
639            rcstr!("process"),
640            rcstr!("next/dist/build/polyfills/process"),
641        ),
642        (
643            rcstr!("querystring"),
644            rcstr!("next/dist/compiled/querystring-es3"),
645        ),
646        (
647            rcstr!("stream"),
648            rcstr!("next/dist/compiled/stream-browserify"),
649        ),
650        (
651            rcstr!("string_decoder"),
652            rcstr!("next/dist/compiled/string_decoder"),
653        ),
654        (rcstr!("sys"), rcstr!("next/dist/compiled/util")),
655        (
656            rcstr!("timers"),
657            rcstr!("next/dist/compiled/timers-browserify"),
658        ),
659        (rcstr!("tty"), rcstr!("next/dist/compiled/tty-browserify")),
660        (rcstr!("url"), rcstr!("next/dist/compiled/native-url")),
661        (rcstr!("util"), rcstr!("next/dist/compiled/util")),
662        (rcstr!("vm"), rcstr!("next/dist/compiled/vm-browserify")),
663        (rcstr!("zlib"), rcstr!("next/dist/compiled/browserify-zlib")),
664        (rcstr!("events"), rcstr!("next/dist/compiled/events")),
665        (
666            rcstr!("setImmediate"),
667            rcstr!("next/dist/compiled/setimmediate"),
668        ),
669    ]
670});
671
672async fn insert_next_server_special_aliases(
673    import_map: &mut ImportMap,
674    project_path: FileSystemPath,
675    ty: ServerContextType,
676    runtime: NextRuntime,
677    next_config: Vc<NextConfig>,
678    collected_root_params: Option<Vc<CollectedRootParams>>,
679) -> Result<()> {
680    let external_cjs_if_node = move |context_dir: FileSystemPath, request: RcStr| match runtime {
681        NextRuntime::Edge => request_to_import_mapping(context_dir, request),
682        NextRuntime::NodeJs => external_request_to_cjs_import_mapping(context_dir, request),
683    };
684    let external_esm_if_node = move |context_dir: FileSystemPath, request: RcStr| match runtime {
685        NextRuntime::Edge => request_to_import_mapping(context_dir, request),
686        NextRuntime::NodeJs => external_request_to_esm_import_mapping(context_dir, request),
687    };
688
689    import_map.insert_exact_alias(
690        rcstr!("next/dist/compiled/@vercel/og/index.node.js"),
691        external_esm_if_node(
692            project_path.clone(),
693            rcstr!("next/dist/compiled/@vercel/og/index.node.js"),
694        ),
695    );
696
697    import_map.insert_exact_alias(
698        rcstr!("next/dist/server/ReactDOMServerPages"),
699        ImportMapping::Alternatives(vec![
700            request_to_import_mapping(project_path.clone(), rcstr!("react-dom/server.edge")),
701            request_to_import_mapping(project_path.clone(), rcstr!("react-dom/server.browser")),
702        ])
703        .resolved_cell(),
704    );
705
706    match &ty {
707        ServerContextType::Pages { .. } | ServerContextType::PagesApi { .. } => {}
708        ServerContextType::PagesData { .. } => {}
709        // the logic closely follows the one in createRSCAliases in webpack-config.ts
710        ServerContextType::AppSSR { app_dir }
711        | ServerContextType::AppRSC { app_dir, .. }
712        | ServerContextType::AppRoute { app_dir, .. } => {
713            let next_package = get_next_package(app_dir.clone()).owned().await?;
714            import_map.insert_exact_alias(
715                rcstr!("styled-jsx"),
716                request_to_import_mapping(next_package.clone(), rcstr!("styled-jsx")),
717            );
718            import_map.insert_wildcard_alias(
719                rcstr!("styled-jsx/"),
720                request_to_import_mapping(next_package.clone(), rcstr!("styled-jsx/*")),
721            );
722
723            rsc_aliases(
724                import_map,
725                project_path.clone(),
726                ty.clone(),
727                runtime,
728                next_config,
729            )
730            .await?;
731        }
732        ServerContextType::Middleware { .. } | ServerContextType::Instrumentation { .. } => {
733            rsc_aliases(
734                import_map,
735                project_path.clone(),
736                ty.clone(),
737                runtime,
738                next_config,
739            )
740            .await?;
741        }
742    }
743
744    // see https://github.com/vercel/next.js/blob/8013ef7372fc545d49dbd060461224ceb563b454/packages/next/src/build/webpack-config.ts#L1449-L1531
745    // Sets runtime aliases for the import to client|server-only. Depends on the
746    // context, it'll resolve to the noop where it's allowed, or aliased into
747    // the error which throws a runtime error. This works with in combination of
748    // build-time error as well, refer https://github.com/vercel/next.js/blob/0060de1c4905593ea875fa7250d4b5d5ce10897d/packages/next-swc/crates/next-core/src/next_server/context.rs#L103
749    match &ty {
750        ServerContextType::Pages { .. } => {
751            insert_exact_alias_map(
752                import_map,
753                project_path.clone(),
754                fxindexmap! {
755                    rcstr!("server-only") => rcstr!("next/dist/compiled/server-only/empty"),
756                    rcstr!("client-only") => rcstr!("next/dist/compiled/client-only/index"),
757                    rcstr!("next/dist/compiled/server-only") => rcstr!("next/dist/compiled/server-only/empty"),
758                    rcstr!("next/dist/compiled/client-only") => rcstr!("next/dist/compiled/client-only/index"),
759                },
760            );
761        }
762        ServerContextType::PagesData { .. }
763        | ServerContextType::PagesApi { .. }
764        | ServerContextType::AppRSC { .. }
765        | ServerContextType::AppRoute { .. }
766        | ServerContextType::Middleware { .. }
767        | ServerContextType::Instrumentation { .. } => {
768            insert_exact_alias_map(
769                import_map,
770                project_path.clone(),
771                fxindexmap! {
772                    rcstr!("server-only") => rcstr!("next/dist/compiled/server-only/empty"),
773                    rcstr!("client-only") => rcstr!("next/dist/compiled/client-only/error"),
774                    rcstr!("next/dist/compiled/server-only") => rcstr!("next/dist/compiled/server-only/empty"),
775                    rcstr!("next/dist/compiled/client-only") => rcstr!("next/dist/compiled/client-only/error"),
776                },
777            );
778        }
779        ServerContextType::AppSSR { .. } => {
780            insert_exact_alias_map(
781                import_map,
782                project_path.clone(),
783                fxindexmap! {
784                    rcstr!("server-only") => rcstr!("next/dist/compiled/server-only/index"),
785                    rcstr!("client-only") => rcstr!("next/dist/compiled/client-only/index"),
786                    rcstr!("next/dist/compiled/server-only") => rcstr!("next/dist/compiled/server-only/index"),
787                    rcstr!("next/dist/compiled/client-only") => rcstr!("next/dist/compiled/client-only/index"),
788                },
789            );
790        }
791    }
792
793    insert_next_root_params_mapping(
794        import_map,
795        next_config.enable_root_params(),
796        Either::Left(ty),
797        collected_root_params,
798    )
799    .await?;
800
801    import_map.insert_exact_alias(
802        rcstr!("@vercel/og"),
803        external_cjs_if_node(
804            project_path.clone(),
805            rcstr!("next/dist/server/og/image-response"),
806        ),
807    );
808
809    import_map.insert_exact_alias(
810        rcstr!("next/dist/compiled/next-devtools"),
811        request_to_import_mapping(
812            project_path.clone(),
813            rcstr!("next/dist/next-devtools/dev-overlay.shim.js"),
814        ),
815    );
816
817    Ok(())
818}
819
820async fn get_react_client_package(next_config: Vc<NextConfig>) -> Result<&'static str> {
821    let react_production_profiling = *next_config.enable_react_production_profiling().await?;
822    let react_client_package = if react_production_profiling {
823        "profiling"
824    } else {
825        "client"
826    };
827
828    Ok(react_client_package)
829}
830
831// Use createVendoredReactAliases in file:///./../../../packages/next/src/build/create-compiler-aliases.ts
832// as the source of truth.
833async fn apply_vendored_react_aliases_server(
834    import_map: &mut ImportMap,
835    project_path: FileSystemPath,
836    ty: ServerContextType,
837    runtime: NextRuntime,
838    next_config: Vc<NextConfig>,
839) -> Result<()> {
840    let ppr = *next_config.enable_ppr().await?;
841    let taint = *next_config.enable_taint().await?;
842    let router_bfcache = *next_config.enable_router_bfcache().await?;
843    let view_transition = *next_config.enable_view_transition().await?;
844    let react_channel = if ppr || taint || view_transition || router_bfcache {
845        "-experimental"
846    } else {
847        ""
848    };
849    let react_condition = if ty.should_use_react_server_condition() {
850        "server"
851    } else {
852        "client"
853    };
854
855    // ✅ Correct alias
856    // ❌ Incorrect alias i.e. importing this entrypoint should throw an error.
857    // ❔ Alias that may produce correct code in certain conditions.Keep until react-markup is
858    // available.
859
860    let mut react_alias = FxIndexMap::default();
861    if runtime == NextRuntime::NodeJs && react_condition == "client" {
862        react_alias.extend(fxindexmap! {
863            // file:///./../../../packages/next/src/compiled/react/package.json
864            rcstr!("react") =>                                  /* ✅ */ rcstr!("next/dist/server/route-modules/app-page/vendored/ssr/react"),
865            rcstr!("react/compiler-runtime") =>                 /* ✅ */ rcstr!("next/dist/server/route-modules/app-page/vendored/ssr/react-compiler-runtime"),
866            rcstr!("react/jsx-dev-runtime") =>                  /* ✅ */ rcstr!("next/dist/server/route-modules/app-page/vendored/ssr/react-jsx-dev-runtime"),
867            rcstr!("react/jsx-runtime") =>                      /* ✅ */ rcstr!("next/dist/server/route-modules/app-page/vendored/ssr/react-jsx-runtime"),
868            // file:///./../../../packages/next/src/compiled/react-dom/package.json
869            rcstr!("react-dom") =>                              /* ✅ */ rcstr!("next/dist/server/route-modules/app-page/vendored/ssr/react-dom"),
870            rcstr!("react-dom/client") =>                       /* ❔ */ format!("next/dist/compiled/react-dom{react_channel}/client").into(),
871            rcstr!("react-dom/server") =>                       /* ❔ */ format!("next/dist/compiled/react-dom{react_channel}/server.node").into(),
872            rcstr!("react-dom/server.browser") =>               /* ❔ */ format!("next/dist/compiled/react-dom{react_channel}/server.browser").into(),
873            // TODO: Use build without legacy APIs
874            rcstr!("react-dom/server.edge") =>                  /* ❔ */ format!("next/dist/compiled/react-dom{react_channel}/server.edge").into(),
875            rcstr!("react-dom/static") =>                       /* ❔ */ format!("next/dist/compiled/react-dom{react_channel}/static.node").into(),
876            rcstr!("react-dom/static.browser") =>               /* ❔ */ format!("next/dist/compiled/react-dom{react_channel}/static.browser").into(),
877            rcstr!("react-dom/static.edge") =>                  /* ❔ */ format!("next/dist/compiled/react-dom{react_channel}/static.edge").into(),
878            // file:///./../../../packages/next/src/compiled/react-server-dom-webpack/package.json
879            rcstr!("react-server-dom-webpack/client") =>        /* ✅ */ rcstr!("next/dist/server/route-modules/app-page/vendored/ssr/react-server-dom-turbopack-client"),
880            rcstr!("react-server-dom-webpack/server") =>        /* ❌ */ format!("next/dist/compiled/react-server-dom-turbopack{react_channel}/server.node").into(),
881            rcstr!("react-server-dom-webpack/server.node") =>   /* ❌ */ format!("next/dist/compiled/react-server-dom-turbopack{react_channel}/server.node").into(),
882            rcstr!("react-server-dom-webpack/static") =>        /* ❌ */ format!("next/dist/compiled/react-server-dom-turbopack{react_channel}/static.node").into(),
883            rcstr!("react-server-dom-turbopack/client") =>      /* ✅ */ rcstr!("next/dist/server/route-modules/app-page/vendored/ssr/react-server-dom-turbopack-client"),
884            rcstr!("react-server-dom-turbopack/server") =>      /* ❌ */ format!("next/dist/compiled/react-server-dom-turbopack{react_channel}/server.node").into(),
885            rcstr!("react-server-dom-turbopack/server.node") => /* ❌ */ format!("next/dist/compiled/react-server-dom-turbopack{react_channel}/server.node").into(),
886            rcstr!("react-server-dom-turbopack/static.edge") => /* ❌ */ format!("next/dist/compiled/react-server-dom-turbopack{react_channel}/static.edge").into(),
887        })
888    } else if runtime == NextRuntime::NodeJs && react_condition == "server" {
889        react_alias.extend(fxindexmap! {
890            // file:///./../../../packages/next/src/compiled/react/package.json
891            rcstr!("react") =>                                  /* ✅ */ rcstr!("next/dist/server/route-modules/app-page/vendored/rsc/react"),
892            rcstr!("react/compiler-runtime") =>                 /* ✅ */ rcstr!("next/dist/server/route-modules/app-page/vendored/rsc/react-compiler-runtime"),
893            rcstr!("react/jsx-dev-runtime") =>                  /* ✅ */ rcstr!("next/dist/server/route-modules/app-page/vendored/rsc/react-jsx-dev-runtime"),
894            rcstr!("react/jsx-runtime") =>                      /* ✅ */ rcstr!("next/dist/server/route-modules/app-page/vendored/rsc/react-jsx-runtime"),
895            // file:///./../../../packages/next/src/compiled/react-dom/package.json
896            rcstr!("react-dom") =>                              /* ✅ */ rcstr!("next/dist/server/route-modules/app-page/vendored/rsc/react-dom"),
897            rcstr!("react-dom/client") =>                       /* ❔ */ format!("next/dist/compiled/react-dom{react_channel}/client").into(),
898            rcstr!("react-dom/server") =>                       /* ❔ */ format!("next/dist/compiled/react-dom{react_channel}/server.node").into(),
899            rcstr!("react-dom/server.browser") =>               /* ❔ */ format!("next/dist/compiled/react-dom{react_channel}/server.browser").into(),
900            // TODO: Use build without legacy APIs
901            rcstr!("react-dom/server.edge") =>                  /* ❔ */ format!("next/dist/compiled/react-dom{react_channel}/server.edge").into(),
902            rcstr!("react-dom/static") =>                       /* ❔ */ format!("next/dist/compiled/react-dom{react_channel}/static.node").into(),
903            rcstr!("react-dom/static.browser") =>               /* ❔ */ format!("next/dist/compiled/react-dom{react_channel}/static.browser").into(),
904            rcstr!("react-dom/static.edge") =>                  /* ❔ */ format!("next/dist/compiled/react-dom{react_channel}/static.edge").into(),
905            // file:///./../../../packages/next/src/compiled/react-server-dom-webpack/package.json
906            rcstr!("react-server-dom-webpack/client") =>        /* ❔ */ format!("next/dist/compiled/react-server-dom-turbopack{react_channel}/client.node").into(),
907            rcstr!("react-server-dom-webpack/server") =>        /* ✅ */ rcstr!("next/dist/server/route-modules/app-page/vendored/rsc/react-server-dom-turbopack-server"),
908            rcstr!("react-server-dom-webpack/server.node") =>   /* ✅ */ rcstr!("next/dist/server/route-modules/app-page/vendored/rsc/react-server-dom-turbopack-server"),
909            rcstr!("react-server-dom-webpack/static") =>        /* ✅ */ rcstr!("next/dist/server/route-modules/app-page/vendored/rsc/react-server-dom-turbopack-static"),
910            rcstr!("react-server-dom-turbopack/client") =>      /* ❔ */ format!("next/dist/compiled/react-server-dom-turbopack{react_channel}/client.node").into(),
911            rcstr!("react-server-dom-turbopack/server") =>      /* ✅ */ rcstr!("next/dist/server/route-modules/app-page/vendored/rsc/react-server-dom-turbopack-server"),
912            rcstr!("react-server-dom-turbopack/server.node") => /* ✅ */ rcstr!("next/dist/server/route-modules/app-page/vendored/rsc/react-server-dom-turbopack-server"),
913            rcstr!("react-server-dom-turbopack/static") =>      /* ✅ */ rcstr!("next/dist/server/route-modules/app-page/vendored/rsc/react-server-dom-turbopack-static"),
914
915            // Needed to make `react-dom/server` work.
916            // TODO: really?
917                rcstr!("next/dist/compiled/react") => rcstr!("next/dist/compiled/react/index.js"),
918        })
919    } else if runtime == NextRuntime::Edge && react_condition == "client" {
920        react_alias.extend(fxindexmap! {
921            // file:///./../../../packages/next/src/compiled/react/package.json
922            rcstr!("react") =>                                  /* ✅ */ format!("next/dist/compiled/react{react_channel}").into(),
923            rcstr!("react/compiler-runtime") =>                 /* ✅ */ format!("next/dist/compiled/react{react_channel}/compiler-runtime").into(),
924            rcstr!("react/jsx-dev-runtime") =>                  /* ✅ */ format!("next/dist/compiled/react{react_channel}/jsx-dev-runtime").into(),
925            rcstr!("react/jsx-runtime") =>                      /* ✅ */ format!("next/dist/compiled/react{react_channel}/jsx-runtime").into(),
926            // file:///./../../../packages/next/src/compiled/react-dom/package.json
927            rcstr!("react-dom") =>                              /* ✅ */ format!("next/dist/compiled/react-dom{react_channel}").into(),
928            rcstr!("react-dom/client") =>                       /* ✅ */ format!("next/dist/compiled/react-dom{react_channel}/client").into(),
929            rcstr!("react-dom/server") =>                       /* ✅ */ format!("next/dist/compiled/react-dom{react_channel}/server.edge").into(),
930            rcstr!("react-dom/server.browser") =>               /* ✅ */ format!("next/dist/compiled/react-dom{react_channel}/server.browser").into(),
931            // TODO: Use build without legacy APIs
932            rcstr!("react-dom/server.edge") =>                  /* ✅ */ format!("next/dist/compiled/react-dom{react_channel}/server.edge").into(),
933            rcstr!("react-dom/static") =>                       /* ✅ */ format!("next/dist/compiled/react-dom{react_channel}/static.edge").into(),
934            rcstr!("react-dom/static.browser") =>               /* ✅ */ format!("next/dist/compiled/react-dom{react_channel}/static.browser").into(),
935            rcstr!("react-dom/static.edge") =>                  /* ✅ */ format!("next/dist/compiled/react-dom{react_channel}/static.edge").into(),
936            // file:///./../../../packages/next/src/compiled/react-server-dom-webpack/package.json
937            rcstr!("react-server-dom-webpack/client") =>        /* ✅ */ format!("next/dist/compiled/react-server-dom-turbopack{react_channel}/client.edge").into(),
938            rcstr!("react-server-dom-webpack/server") =>        /* ❌ */ format!("next/dist/compiled/react-server-dom-turbopack{react_channel}/server.edge").into(),
939            rcstr!("react-server-dom-webpack/server.node") =>   /* ❌ */ format!("next/dist/compiled/react-server-dom-turbopack{react_channel}/server.node").into(),
940            rcstr!("react-server-dom-webpack/static") =>        /* ❌ */ format!("next/dist/compiled/react-server-dom-turbopack{react_channel}/static.edge").into(),
941            rcstr!("react-server-dom-turbopack/client") =>      /* ✅ */ format!("next/dist/compiled/react-server-dom-turbopack{react_channel}/client.edge").into(),
942            rcstr!("react-server-dom-turbopack/server") =>      /* ❌ */ format!("next/dist/compiled/react-server-dom-turbopack{react_channel}/server.edge").into(),
943            rcstr!("react-server-dom-turbopack/server.node") => /* ❌ */ format!("next/dist/compiled/react-server-dom-turbopack{react_channel}/server.node").into(),
944            rcstr!("react-server-dom-turbopack/static") =>      /* ❌ */ format!("next/dist/compiled/react-server-dom-turbopack{react_channel}/static.edge").into(),
945        })
946    } else if runtime == NextRuntime::Edge && react_condition == "server" {
947        react_alias.extend(fxindexmap! {
948            // file:///./../../../packages/next/src/compiled/react/package.json
949            rcstr!("react") =>                                  /* ✅ */ format!("next/dist/compiled/react{react_channel}/react.react-server").into(),
950            rcstr!("react/compiler-runtime") =>                 /* ❌ */ format!("next/dist/compiled/react{react_channel}/compiler-runtime").into(),
951            rcstr!("react/jsx-dev-runtime") =>                  /* ✅ */ format!("next/dist/compiled/react{react_channel}/jsx-dev-runtime.react-server").into(),
952            rcstr!("react/jsx-runtime") =>                      /* ✅ */ format!("next/dist/compiled/react{react_channel}/jsx-runtime.react-server").into(),
953            // file:///./../../../packages/next/src/compiled/react-dom/package.json
954            rcstr!("react-dom") =>                              /* ✅ */ format!("next/dist/compiled/react-dom{react_channel}/react-dom.react-server").into(),
955            rcstr!("react-dom/client") =>                       /* ❌ */ format!("next/dist/compiled/react-dom{react_channel}/client").into(),
956            rcstr!("react-dom/server") =>                       /* ❌ */ format!("next/dist/compiled/react-dom{react_channel}/server.edge").into(),
957            rcstr!("react-dom/server.browser") =>               /* ❌ */ format!("next/dist/compiled/react-dom{react_channel}/server.browser").into(),
958            // TODO: Use build without legacy APIs
959            rcstr!("react-dom/server.edge") =>                  /* ❌ */ format!("next/dist/compiled/react-dom{react_channel}/server.edge").into(),
960            rcstr!("react-dom/static") =>                       /* ❌ */ format!("next/dist/compiled/react-dom{react_channel}/static.edge").into(),
961            rcstr!("react-dom/static.browser") =>               /* ❌ */ format!("next/dist/compiled/react-dom{react_channel}/static.browser").into(),
962            rcstr!("react-dom/static.edge") =>                  /* ❌ */ format!("next/dist/compiled/react-dom{react_channel}/static.edge").into(),
963            // file:///./../../../packages/next/src/compiled/react-server-dom-webpack/package.json
964            rcstr!("react-server-dom-webpack/client") =>        /* ❔ */ format!("next/dist/compiled/react-server-dom-turbopack{react_channel}/client.edge").into(),
965            rcstr!("react-server-dom-webpack/server") =>        /* ✅ */ format!("next/dist/compiled/react-server-dom-turbopack{react_channel}/server.edge").into(),
966            rcstr!("react-server-dom-webpack/server.node") =>   /* ✅ */ format!("next/dist/compiled/react-server-dom-turbopack{react_channel}/server.node").into(),
967            rcstr!("react-server-dom-webpack/static") =>        /* ✅ */ format!("next/dist/compiled/react-server-dom-turbopack{react_channel}/static.edge").into(),
968            rcstr!("react-server-dom-turbopack/client") =>      /* ❔ */ format!("next/dist/compiled/react-server-dom-turbopack{react_channel}/client.edge").into(),
969            rcstr!("react-server-dom-turbopack/server") =>      /* ✅ */ format!("next/dist/compiled/react-server-dom-turbopack{react_channel}/server.edge").into(),
970            rcstr!("react-server-dom-turbopack/server.node") => /* ✅ */ format!("next/dist/compiled/react-server-dom-turbopack{react_channel}/server.node").into(),
971            rcstr!("react-server-dom-turbopack/static") =>      /* ✅ */ format!("next/dist/compiled/react-server-dom-turbopack{react_channel}/static.edge").into(),
972        });
973
974        react_alias.extend(fxindexmap! {
975            // This should just be `next/dist/compiled/react${react_channel}` but how to Rust.
976            rcstr!("next/dist/compiled/react")                               => react_alias["react"].clone(),
977            rcstr!("next/dist/compiled/react-experimental")                  => react_alias["react"].clone(),
978            rcstr!("next/dist/compiled/react/compiler-runtime")              => react_alias["react/compiler-runtime"].clone(),
979            rcstr!("next/dist/compiled/react-experimental/compiler-runtime") => react_alias["react/compiler-runtime"].clone(),
980            rcstr!("next/dist/compiled/react/jsx-dev-runtime")               => react_alias["react/jsx-dev-runtime"].clone(),
981            rcstr!("next/dist/compiled/react-experimental/jsx-dev-runtime")  => react_alias["react/jsx-dev-runtime"].clone(),
982            rcstr!("next/dist/compiled/react/jsx-runtime")                   => react_alias["react/jsx-runtime"].clone(),
983            rcstr!("next/dist/compiled/react-experimental/jsx-runtime")      => react_alias["react/jsx-runtime"].clone(),
984            rcstr!("next/dist/compiled/react-dom")                           => react_alias["react-dom"].clone(),
985            rcstr!("next/dist/compiled/react-dom-experimental")              => react_alias["react-dom"].clone(),
986        });
987    }
988
989    let react_client_package = get_react_client_package(next_config).await?;
990    react_alias.extend(fxindexmap! {
991        rcstr!("react-dom/client") => RcStr::from(format!("next/dist/compiled/react-dom{react_channel}/{react_client_package}")),
992    });
993
994    let mut alias = react_alias;
995    if react_condition == "server" {
996        // This is used in the server runtime to import React Server Components.
997        alias.extend(fxindexmap! {
998            rcstr!("next/navigation") => rcstr!("next/dist/api/navigation.react-server"),
999        });
1000    }
1001
1002    insert_exact_alias_map(import_map, project_path, alias);
1003
1004    Ok(())
1005}
1006
1007async fn rsc_aliases(
1008    import_map: &mut ImportMap,
1009    project_path: FileSystemPath,
1010    ty: ServerContextType,
1011    runtime: NextRuntime,
1012    next_config: Vc<NextConfig>,
1013) -> Result<()> {
1014    apply_vendored_react_aliases_server(
1015        import_map,
1016        project_path.clone(),
1017        ty.clone(),
1018        runtime,
1019        next_config,
1020    )
1021    .await?;
1022
1023    let mut alias = FxIndexMap::default();
1024    if ty.should_use_react_server_condition() {
1025        // This is used in the server runtime to import React Server Components.
1026        alias.extend(fxindexmap! {
1027            rcstr!("next/navigation") => rcstr!("next/dist/api/navigation.react-server"),
1028        });
1029    }
1030
1031    insert_exact_alias_map(import_map, project_path.clone(), alias);
1032
1033    Ok(())
1034}
1035
1036pub fn mdx_import_source_file() -> RcStr {
1037    format!("{VIRTUAL_PACKAGE_NAME}/mdx-import-source").into()
1038}
1039
1040// Insert aliases for Next.js stubs of fetch, object-assign, and url
1041// Keep in sync with getOptimizedModuleAliases in webpack-config.ts
1042async fn insert_optimized_module_aliases(
1043    import_map: &mut ImportMap,
1044    project_path: FileSystemPath,
1045) -> Result<()> {
1046    insert_exact_alias_map(
1047        import_map,
1048        project_path,
1049        fxindexmap! {
1050            rcstr!("unfetch") => rcstr!("next/dist/build/polyfills/fetch/index.js"),
1051            rcstr!("isomorphic-unfetch") => rcstr!("next/dist/build/polyfills/fetch/index.js"),
1052            rcstr!("whatwg-fetch") => rcstr!("next/dist/build/polyfills/fetch/whatwg-fetch.js"),
1053            rcstr!("object-assign") => rcstr!("next/dist/build/polyfills/object-assign.js"),
1054            rcstr!("object.assign/auto") => rcstr!("next/dist/build/polyfills/object.assign/auto.js"),
1055            rcstr!("object.assign/implementation") => rcstr!("next/dist/build/polyfills/object.assign/implementation.js"),
1056            rcstr!("object.assign/polyfill") => rcstr!("next/dist/build/polyfills/object.assign/polyfill.js"),
1057            rcstr!("object.assign/shim") => rcstr!("next/dist/build/polyfills/object.assign/shim.js"),
1058            rcstr!("url") => rcstr!("next/dist/compiled/native-url"),
1059            rcstr!("node:url") => rcstr!("next/dist/compiled/native-url"),
1060        },
1061    );
1062    Ok(())
1063}
1064
1065// Make sure to not add any external requests here.
1066async fn insert_next_shared_aliases(
1067    import_map: &mut ImportMap,
1068    project_path: FileSystemPath,
1069    execution_context: Vc<ExecutionContext>,
1070    next_config: Vc<NextConfig>,
1071    next_mode: Vc<NextMode>,
1072    is_runtime_edge: bool,
1073) -> Result<()> {
1074    let package_root = next_js_fs().root().owned().await?;
1075
1076    insert_alias_to_alternatives(
1077        import_map,
1078        mdx_import_source_file(),
1079        vec![
1080            request_to_import_mapping(project_path.clone(), rcstr!("./mdx-components")),
1081            request_to_import_mapping(project_path.clone(), rcstr!("./src/mdx-components")),
1082            request_to_import_mapping(project_path.clone(), rcstr!("@mdx-js/react")),
1083            request_to_import_mapping(project_path.clone(), rcstr!("@next/mdx/mdx-components.js")),
1084        ],
1085    );
1086
1087    insert_package_alias(
1088        import_map,
1089        &format!("{VIRTUAL_PACKAGE_NAME}/"),
1090        package_root,
1091    );
1092
1093    // NOTE: `@next/font/local` has moved to a BeforeResolve Plugin, so it does not
1094    // have ImportMapping replacers here.
1095    //
1096    // TODO: Add BeforeResolve plugins for `@next/font/google`
1097
1098    let next_font_google_replacer_mapping = ImportMapping::Dynamic(ResolvedVc::upcast(
1099        NextFontGoogleReplacer::new(project_path.clone())
1100            .to_resolved()
1101            .await?,
1102    ))
1103    .resolved_cell();
1104
1105    import_map.insert_alias(
1106        // Request path from js via next-font swc transform
1107        AliasPattern::exact(rcstr!("next/font/google/target.css")),
1108        next_font_google_replacer_mapping,
1109    );
1110
1111    import_map.insert_alias(
1112        // Request path from js via next-font swc transform
1113        AliasPattern::exact(rcstr!("@next/font/google/target.css")),
1114        next_font_google_replacer_mapping,
1115    );
1116
1117    let fetch_client = next_config.fetch_client(execution_context.env());
1118    import_map.insert_alias(
1119        AliasPattern::exact(rcstr!(
1120            "@vercel/turbopack-next/internal/font/google/cssmodule.module.css"
1121        )),
1122        ImportMapping::Dynamic(ResolvedVc::upcast(
1123            NextFontGoogleCssModuleReplacer::new(
1124                project_path.clone(),
1125                execution_context,
1126                next_mode,
1127                fetch_client,
1128            )
1129            .to_resolved()
1130            .await?,
1131        ))
1132        .resolved_cell(),
1133    );
1134
1135    import_map.insert_alias(
1136        AliasPattern::exact(rcstr!(GOOGLE_FONTS_INTERNAL_PREFIX)),
1137        ImportMapping::Dynamic(ResolvedVc::upcast(
1138            NextFontGoogleFontFileReplacer::new(project_path.clone(), fetch_client)
1139                .to_resolved()
1140                .await?,
1141        ))
1142        .resolved_cell(),
1143    );
1144
1145    let next_package = get_next_package(project_path.clone()).owned().await?;
1146    import_map.insert_singleton_alias(rcstr!("@swc/helpers"), next_package.clone());
1147    import_map.insert_singleton_alias(rcstr!("styled-jsx"), next_package.clone());
1148    import_map.insert_singleton_alias(rcstr!("next"), project_path.clone());
1149    import_map.insert_singleton_alias(rcstr!("react"), project_path.clone());
1150    import_map.insert_singleton_alias(rcstr!("react-dom"), project_path.clone());
1151    let react_client_package = get_react_client_package(next_config).await?;
1152    import_map.insert_exact_alias(
1153        rcstr!("react-dom/client"),
1154        request_to_import_mapping(
1155            project_path.clone(),
1156            format!("react-dom/{react_client_package}").into(),
1157        ),
1158    );
1159
1160    import_map.insert_alias(
1161        // Make sure you can't import custom server as it'll cause all Next.js internals to be
1162        // bundled which doesn't work.
1163        AliasPattern::exact(rcstr!("next")),
1164        ImportMapping::Empty.resolved_cell(),
1165    );
1166
1167    //https://github.com/vercel/next.js/blob/f94d4f93e4802f951063cfa3351dd5a2325724b3/packages/next/src/build/webpack-config.ts#L1196
1168    import_map.insert_exact_alias(
1169        rcstr!("setimmediate"),
1170        request_to_import_mapping(
1171            project_path.clone(),
1172            rcstr!("next/dist/compiled/setimmediate"),
1173        ),
1174    );
1175
1176    import_map.insert_exact_alias(
1177        rcstr!("private-next-rsc-server-reference"),
1178        request_to_import_mapping(
1179            project_path.clone(),
1180            rcstr!("next/dist/build/webpack/loaders/next-flight-loader/server-reference"),
1181        ),
1182    );
1183    import_map.insert_exact_alias(
1184        rcstr!("private-next-rsc-action-client-wrapper"),
1185        request_to_import_mapping(
1186            project_path.clone(),
1187            rcstr!("next/dist/build/webpack/loaders/next-flight-loader/action-client-wrapper"),
1188        ),
1189    );
1190    import_map.insert_exact_alias(
1191        rcstr!("private-next-rsc-action-validate"),
1192        request_to_import_mapping(
1193            project_path.clone(),
1194            rcstr!("next/dist/build/webpack/loaders/next-flight-loader/action-validate"),
1195        ),
1196    );
1197    import_map.insert_exact_alias(
1198        rcstr!("private-next-rsc-action-encryption"),
1199        request_to_import_mapping(
1200            project_path.clone(),
1201            rcstr!("next/dist/server/app-render/encryption"),
1202        ),
1203    );
1204    import_map.insert_exact_alias(
1205        rcstr!("private-next-rsc-cache-wrapper"),
1206        request_to_import_mapping(
1207            project_path.clone(),
1208            rcstr!("next/dist/build/webpack/loaders/next-flight-loader/cache-wrapper"),
1209        ),
1210    );
1211    import_map.insert_exact_alias(
1212        rcstr!("private-next-rsc-track-dynamic-import"),
1213        request_to_import_mapping(
1214            project_path.clone(),
1215            rcstr!("next/dist/build/webpack/loaders/next-flight-loader/track-dynamic-import"),
1216        ),
1217    );
1218
1219    insert_turbopack_dev_alias(import_map).await?;
1220    insert_package_alias(
1221        import_map,
1222        "@vercel/turbopack-node/",
1223        turbopack_node::embed_js::embed_fs().root().owned().await?,
1224    );
1225
1226    let image_config = next_config.image_config().await?;
1227    if let Some(loader_file) = image_config.loader_file.as_deref().map(RcStr::from) {
1228        import_map.insert_exact_alias(
1229            rcstr!("next/dist/shared/lib/image-loader"),
1230            request_to_import_mapping(project_path.clone(), loader_file.clone()),
1231        );
1232
1233        if is_runtime_edge {
1234            import_map.insert_exact_alias(
1235                rcstr!("next/dist/esm/shared/lib/image-loader"),
1236                request_to_import_mapping(project_path.clone(), loader_file),
1237            );
1238        }
1239    }
1240
1241    Ok(())
1242}
1243
1244#[turbo_tasks::function]
1245pub async fn get_next_package(context_directory: FileSystemPath) -> Result<Vc<FileSystemPath>> {
1246    let result = resolve(
1247        context_directory.clone(),
1248        ReferenceType::CommonJs(CommonJsReferenceSubType::Undefined),
1249        Request::parse(Pattern::Constant(rcstr!("next/package.json"))),
1250        node_cjs_resolve_options(context_directory.root().owned().await?),
1251    );
1252    let source = result
1253        .first_source()
1254        .await?
1255        .context("Next.js package not found")?;
1256    Ok(source.ident().path().await?.parent().cell())
1257}
1258
1259pub async fn insert_alias_option<const N: usize>(
1260    import_map: &mut ImportMap,
1261    project_path: &FileSystemPath,
1262    alias_options: Vc<ResolveAliasMap>,
1263    conditions: [&'static str; N],
1264) -> Result<()> {
1265    let conditions = BTreeMap::from(conditions.map(|c| (c.into(), ConditionValue::Set)));
1266    for (alias, value) in &alias_options.await? {
1267        if let Some(mapping) = export_value_to_import_mapping(value, &conditions, project_path) {
1268            import_map.insert_alias(alias, mapping);
1269        }
1270    }
1271    Ok(())
1272}
1273
1274fn export_value_to_import_mapping(
1275    value: &SubpathValue,
1276    conditions: &BTreeMap<RcStr, ConditionValue>,
1277    project_path: &FileSystemPath,
1278) -> Option<ResolvedVc<ImportMapping>> {
1279    let mut result = Vec::new();
1280    value.add_results(
1281        conditions,
1282        &ConditionValue::Unset,
1283        &mut FxHashMap::default(),
1284        &mut result,
1285    );
1286    if result.is_empty() {
1287        None
1288    } else {
1289        Some(if result.len() == 1 {
1290            ImportMapping::PrimaryAlternative(result[0].0.into(), Some(project_path.clone()))
1291                .resolved_cell()
1292        } else {
1293            ImportMapping::Alternatives(
1294                result
1295                    .iter()
1296                    .map(|(m, _)| {
1297                        ImportMapping::PrimaryAlternative((*m).into(), Some(project_path.clone()))
1298                            .resolved_cell()
1299                    })
1300                    .collect(),
1301            )
1302            .resolved_cell()
1303        })
1304    }
1305}
1306
1307fn insert_exact_alias_map(
1308    import_map: &mut ImportMap,
1309    project_path: FileSystemPath,
1310    map: FxIndexMap<RcStr, RcStr>,
1311) {
1312    for (pattern, request) in map {
1313        import_map.insert_exact_alias(
1314            pattern,
1315            request_to_import_mapping(project_path.clone(), request),
1316        );
1317    }
1318}
1319
1320fn insert_wildcard_alias_map(
1321    import_map: &mut ImportMap,
1322    project_path: FileSystemPath,
1323    map: FxIndexMap<RcStr, RcStr>,
1324) {
1325    for (pattern, request) in map {
1326        import_map.insert_wildcard_alias(
1327            pattern,
1328            request_to_import_mapping(project_path.clone(), request),
1329        );
1330    }
1331}
1332
1333/// Inserts an alias to an alternative of import mappings into an import map.
1334fn insert_alias_to_alternatives<'a>(
1335    import_map: &mut ImportMap,
1336    alias: impl Into<RcStr> + 'a,
1337    alternatives: Vec<ResolvedVc<ImportMapping>>,
1338) {
1339    import_map.insert_exact_alias(
1340        alias.into(),
1341        ImportMapping::Alternatives(alternatives).resolved_cell(),
1342    );
1343}
1344
1345/// Inserts an alias to an import mapping into an import map.
1346fn insert_package_alias(import_map: &mut ImportMap, prefix: &str, package_root: FileSystemPath) {
1347    import_map.insert_wildcard_alias(
1348        prefix,
1349        ImportMapping::PrimaryAlternative(rcstr!("./*"), Some(package_root)).resolved_cell(),
1350    );
1351}
1352
1353/// Inserts an alias to @vercel/turbopack-dev into an import map.
1354async fn insert_turbopack_dev_alias(import_map: &mut ImportMap) -> Result<()> {
1355    insert_package_alias(
1356        import_map,
1357        "@vercel/turbopack-ecmascript-runtime/",
1358        turbopack_ecmascript_runtime::embed_fs()
1359            .root()
1360            .owned()
1361            .await?,
1362    );
1363    Ok(())
1364}
1365
1366/// Handles instrumentation-client.ts bundling logic
1367async fn insert_instrumentation_client_alias(
1368    import_map: &mut ImportMap,
1369    project_path: FileSystemPath,
1370) -> Result<()> {
1371    insert_alias_to_alternatives(
1372        import_map,
1373        rcstr!("private-next-instrumentation-client"),
1374        vec![
1375            request_to_import_mapping(project_path.clone(), rcstr!("./src/instrumentation-client")),
1376            request_to_import_mapping(
1377                project_path.clone(),
1378                rcstr!("./src/instrumentation-client.ts"),
1379            ),
1380            request_to_import_mapping(project_path.clone(), rcstr!("./instrumentation-client")),
1381            request_to_import_mapping(project_path.clone(), rcstr!("./instrumentation-client.ts")),
1382            ImportMapping::Ignore.resolved_cell(),
1383        ],
1384    );
1385
1386    Ok(())
1387}
1388
1389// To alias e.g. both `import "next/link"` and `import "next/link.js"`
1390fn insert_exact_alias_or_js(
1391    import_map: &mut ImportMap,
1392    pattern: RcStr,
1393    mapping: ResolvedVc<ImportMapping>,
1394) {
1395    import_map.insert_exact_alias(format!("{pattern}.js"), mapping);
1396    import_map.insert_exact_alias(pattern, mapping);
1397}
1398
1399/// Creates a direct import mapping to the result of resolving a request
1400/// in a context.
1401fn request_to_import_mapping(
1402    context_path: FileSystemPath,
1403    request: RcStr,
1404) -> ResolvedVc<ImportMapping> {
1405    ImportMapping::PrimaryAlternative(request, Some(context_path)).resolved_cell()
1406}
1407
1408/// Creates a direct import mapping to the result of resolving an external
1409/// request.
1410fn external_request_to_cjs_import_mapping(
1411    context_dir: FileSystemPath,
1412    request: RcStr,
1413) -> ResolvedVc<ImportMapping> {
1414    ImportMapping::PrimaryAlternativeExternal {
1415        name: Some(request),
1416        ty: ExternalType::CommonJs,
1417        traced: ExternalTraced::Traced,
1418        lookup_dir: context_dir,
1419    }
1420    .resolved_cell()
1421}
1422
1423/// Creates a direct import mapping to the result of resolving an external
1424/// request.
1425fn external_request_to_esm_import_mapping(
1426    context_dir: FileSystemPath,
1427    request: RcStr,
1428) -> ResolvedVc<ImportMapping> {
1429    ImportMapping::PrimaryAlternativeExternal {
1430        name: Some(request),
1431        ty: ExternalType::EcmaScriptModule,
1432        traced: ExternalTraced::Traced,
1433        lookup_dir: context_dir,
1434    }
1435    .resolved_cell()
1436}