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