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