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