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