next_core/
next_import_map.rs

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