next_core/
next_import_map.rs

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