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