Skip to main content

next_core/
next_import_map.rs

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