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