Skip to main content

next_core/
next_import_map.rs

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