Skip to main content

next_core/
next_import_map.rs

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