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