1use std::time::Duration;
2
3use anyhow::{Context, Result, bail};
4use indexmap::map::Entry;
5use next_core::{
6 all_assets_from_entries,
7 app_structure::find_app_dir,
8 emit_assets, get_edge_chunking_context, get_edge_chunking_context_with_client_assets,
9 get_edge_compile_time_info, get_edge_resolve_options_context,
10 instrumentation::instrumentation_files,
11 middleware::middleware_files,
12 mode::NextMode,
13 next_client::{
14 ClientChunkingContextOptions, get_client_chunking_context, get_client_compile_time_info,
15 },
16 next_config::{JsConfig, ModuleIds as ModuleIdStrategyConfig, NextConfig},
17 next_edge::context::EdgeChunkingContextOptions,
18 next_server::{
19 ServerChunkingContextOptions, ServerContextType, get_server_chunking_context,
20 get_server_chunking_context_with_client_assets, get_server_compile_time_info,
21 get_server_module_options_context, get_server_resolve_options_context,
22 },
23 next_telemetry::NextFeatureTelemetry,
24 util::{NextRuntime, OptionEnvMap, parse_config_from_source},
25};
26use serde::{Deserialize, Serialize};
27use tracing::Instrument;
28use turbo_rcstr::{RcStr, rcstr};
29use turbo_tasks::{
30 Completion, Completions, FxIndexMap, IntoTraitRef, NonLocalValue, OperationValue, OperationVc,
31 ReadRef, ResolvedVc, State, TaskInput, TransientInstance, TryFlatJoinIterExt, Vc,
32 debug::ValueDebugFormat,
33 fxindexmap,
34 graph::{AdjacencyMap, GraphTraversal},
35 mark_root,
36 trace::TraceRawVcs,
37};
38use turbo_tasks_env::{EnvMap, ProcessEnv};
39use turbo_tasks_fs::{DiskFileSystem, FileSystem, FileSystemPath, VirtualFileSystem, invalidation};
40use turbo_unix_path::{join_path, unix_to_sys};
41use turbopack::{
42 ModuleAssetContext, evaluate_context::node_build_environment,
43 global_module_ids::get_global_module_id_strategy, transition::TransitionOptions,
44};
45use turbopack_core::{
46 PROJECT_FILESYSTEM_NAME,
47 changed::content_changed,
48 chunk::{
49 ChunkingContext, EvaluatableAssets, SourceMapsType,
50 module_id_strategies::{DevModuleIdStrategy, ModuleIdStrategy},
51 },
52 compile_time_info::CompileTimeInfo,
53 context::AssetContext,
54 diagnostics::DiagnosticExt,
55 environment::NodeJsVersion,
56 file_source::FileSource,
57 ident::Layer,
58 issue::{
59 Issue, IssueDescriptionExt, IssueExt, IssueSeverity, IssueStage, OptionStyledString,
60 StyledString,
61 },
62 module::Module,
63 module_graph::{
64 GraphEntries, ModuleGraph, SingleModuleGraph, VisitedModules,
65 chunk_group_info::ChunkGroupEntry,
66 export_usage::{OptionExportUsageInfo, compute_export_usage_info},
67 },
68 output::{OutputAsset, OutputAssets},
69 reference_type::{EntryReferenceSubType, ReferenceType},
70 resolve::{FindContextFileResult, find_context_file},
71 source_map::OptionStringifiedSourceMap,
72 version::{
73 NotFoundVersion, OptionVersionedContent, Update, Version, VersionState, VersionedContent,
74 },
75};
76use turbopack_node::execution_context::ExecutionContext;
77use turbopack_nodejs::NodeJsChunkingContext;
78
79use crate::{
80 app::{AppProject, OptionAppProject},
81 empty::EmptyEndpoint,
82 entrypoints::Entrypoints,
83 instrumentation::InstrumentationEndpoint,
84 middleware::MiddlewareEndpoint,
85 pages::PagesProject,
86 route::{AppPageRoute, Endpoint, Endpoints, Route},
87 versioned_content_map::VersionedContentMap,
88};
89
90#[derive(
91 Debug,
92 Serialize,
93 Deserialize,
94 Clone,
95 TaskInput,
96 PartialEq,
97 Eq,
98 Hash,
99 TraceRawVcs,
100 NonLocalValue,
101 OperationValue,
102)]
103#[serde(rename_all = "camelCase")]
104pub struct DraftModeOptions {
105 pub preview_mode_id: RcStr,
106 pub preview_mode_encryption_key: RcStr,
107 pub preview_mode_signing_key: RcStr,
108}
109
110#[derive(
111 Debug,
112 Default,
113 Serialize,
114 Deserialize,
115 Copy,
116 Clone,
117 TaskInput,
118 PartialEq,
119 Eq,
120 Hash,
121 TraceRawVcs,
122 NonLocalValue,
123 OperationValue,
124)]
125#[serde(rename_all = "camelCase")]
126pub struct WatchOptions {
127 pub enable: bool,
129
130 pub poll_interval: Option<Duration>,
133}
134
135#[derive(
136 Debug,
137 Serialize,
138 Deserialize,
139 Clone,
140 TaskInput,
141 PartialEq,
142 Eq,
143 Hash,
144 TraceRawVcs,
145 NonLocalValue,
146 OperationValue,
147)]
148#[serde(rename_all = "camelCase")]
149pub struct ProjectOptions {
150 pub root_path: RcStr,
154
155 pub project_path: RcStr,
158
159 pub next_config: RcStr,
161
162 pub js_config: RcStr,
164
165 pub env: Vec<(RcStr, RcStr)>,
167
168 pub define_env: DefineEnv,
171
172 pub watch: WatchOptions,
174
175 pub dev: bool,
177
178 pub encryption_key: RcStr,
180
181 pub build_id: RcStr,
183
184 pub preview_props: DraftModeOptions,
186
187 pub browserslist_query: RcStr,
189
190 pub no_mangling: bool,
194
195 pub current_node_js_version: RcStr,
197}
198
199#[derive(
200 Debug, Serialize, Deserialize, Clone, TaskInput, PartialEq, Eq, Hash, TraceRawVcs, NonLocalValue,
201)]
202#[serde(rename_all = "camelCase")]
203pub struct PartialProjectOptions {
204 pub root_path: Option<RcStr>,
207
208 pub project_path: Option<RcStr>,
210
211 pub next_config: Option<RcStr>,
213
214 pub js_config: Option<RcStr>,
216
217 pub env: Option<Vec<(RcStr, RcStr)>>,
219
220 pub define_env: Option<DefineEnv>,
223
224 pub watch: Option<WatchOptions>,
226
227 pub dev: Option<bool>,
229
230 pub encryption_key: Option<RcStr>,
232
233 pub build_id: Option<RcStr>,
235
236 pub preview_props: Option<DraftModeOptions>,
238}
239
240#[derive(
241 Debug,
242 Serialize,
243 Deserialize,
244 Clone,
245 TaskInput,
246 PartialEq,
247 Eq,
248 Hash,
249 TraceRawVcs,
250 NonLocalValue,
251 OperationValue,
252)]
253#[serde(rename_all = "camelCase")]
254pub struct DefineEnv {
255 pub client: Vec<(RcStr, Option<RcStr>)>,
256 pub edge: Vec<(RcStr, Option<RcStr>)>,
257 pub nodejs: Vec<(RcStr, Option<RcStr>)>,
258}
259
260#[derive(Serialize, Deserialize, TraceRawVcs, PartialEq, Eq, ValueDebugFormat, NonLocalValue)]
261pub struct Middleware {
262 pub endpoint: ResolvedVc<Box<dyn Endpoint>>,
263}
264
265#[derive(Serialize, Deserialize, TraceRawVcs, PartialEq, Eq, ValueDebugFormat, NonLocalValue)]
266pub struct Instrumentation {
267 pub node_js: ResolvedVc<Box<dyn Endpoint>>,
268 pub edge: ResolvedVc<Box<dyn Endpoint>>,
269}
270
271#[turbo_tasks::value]
272pub struct ProjectContainer {
273 name: RcStr,
274 options_state: State<Option<ProjectOptions>>,
275 versioned_content_map: Option<ResolvedVc<VersionedContentMap>>,
276}
277
278#[turbo_tasks::value_impl]
279impl ProjectContainer {
280 #[turbo_tasks::function]
281 pub fn new(name: RcStr, dev: bool) -> Result<Vc<Self>> {
282 Ok(ProjectContainer {
283 name,
284 versioned_content_map: if dev {
287 Some(VersionedContentMap::new())
288 } else {
289 None
290 },
291 options_state: State::new(None),
292 }
293 .cell())
294 }
295}
296
297#[turbo_tasks::function(operation)]
298fn project_fs_operation(project: ResolvedVc<Project>) -> Vc<DiskFileSystem> {
299 project.project_fs()
300}
301
302#[turbo_tasks::function(operation)]
303fn output_fs_operation(project: ResolvedVc<Project>) -> Vc<DiskFileSystem> {
304 project.project_fs()
305}
306
307impl ProjectContainer {
308 #[tracing::instrument(level = "info", name = "initialize project", skip_all)]
309 pub async fn initialize(self: ResolvedVc<Self>, options: ProjectOptions) -> Result<()> {
310 let watch = options.watch;
311
312 self.await?.options_state.set(Some(options));
313
314 let project = self.project().to_resolved().await?;
315 let project_fs = project_fs_operation(project)
316 .read_strongly_consistent()
317 .await?;
318 if watch.enable {
319 project_fs
320 .start_watching_with_invalidation_reason(watch.poll_interval)
321 .await?;
322 } else {
323 project_fs.invalidate_with_reason(|path| invalidation::Initialize {
324 path: RcStr::from(path.to_string_lossy()),
326 });
327 }
328 let output_fs = output_fs_operation(project)
329 .read_strongly_consistent()
330 .await?;
331 output_fs.invalidate_with_reason(|path| invalidation::Initialize {
332 path: RcStr::from(path.to_string_lossy()),
333 });
334 Ok(())
335 }
336
337 #[tracing::instrument(level = "info", name = "update project", skip_all)]
338 pub async fn update(self: Vc<Self>, options: PartialProjectOptions) -> Result<()> {
339 let PartialProjectOptions {
340 root_path,
341 project_path,
342 next_config,
343 js_config,
344 env,
345 define_env,
346 watch,
347 dev,
348 encryption_key,
349 build_id,
350 preview_props,
351 } = options;
352
353 let this = self.await?;
354
355 let mut new_options = this
356 .options_state
357 .get()
358 .clone()
359 .context("ProjectContainer need to be initialized with initialize()")?;
360
361 if let Some(root_path) = root_path {
362 new_options.root_path = root_path;
363 }
364 if let Some(project_path) = project_path {
365 new_options.project_path = project_path;
366 }
367 if let Some(next_config) = next_config {
368 new_options.next_config = next_config;
369 }
370 if let Some(js_config) = js_config {
371 new_options.js_config = js_config;
372 }
373 if let Some(env) = env {
374 new_options.env = env;
375 }
376 if let Some(define_env) = define_env {
377 new_options.define_env = define_env;
378 }
379 if let Some(watch) = watch {
380 new_options.watch = watch;
381 }
382 if let Some(dev) = dev {
383 new_options.dev = dev;
384 }
385 if let Some(encryption_key) = encryption_key {
386 new_options.encryption_key = encryption_key;
387 }
388 if let Some(build_id) = build_id {
389 new_options.build_id = build_id;
390 }
391 if let Some(preview_props) = preview_props {
392 new_options.preview_props = preview_props;
393 }
394
395 let watch = new_options.watch;
397
398 let project = self.project().to_resolved().await?;
399 let prev_project_fs = project_fs_operation(project)
400 .read_strongly_consistent()
401 .await?;
402 let prev_output_fs = output_fs_operation(project)
403 .read_strongly_consistent()
404 .await?;
405
406 this.options_state.set(Some(new_options));
407 let project = self.project().to_resolved().await?;
408 let project_fs = project_fs_operation(project)
409 .read_strongly_consistent()
410 .await?;
411 let output_fs = output_fs_operation(project)
412 .read_strongly_consistent()
413 .await?;
414
415 if !ReadRef::ptr_eq(&prev_project_fs, &project_fs) {
416 if watch.enable {
417 project_fs
419 .start_watching_with_invalidation_reason(watch.poll_interval)
420 .await?;
421 } else {
422 project_fs.invalidate_with_reason(|path| invalidation::Initialize {
423 path: RcStr::from(path.to_string_lossy()),
425 });
426 }
427 }
428 if !ReadRef::ptr_eq(&prev_output_fs, &output_fs) {
429 prev_output_fs.invalidate_with_reason(|path| invalidation::Initialize {
430 path: RcStr::from(path.to_string_lossy()),
431 });
432 }
433
434 Ok(())
435 }
436}
437
438#[turbo_tasks::value_impl]
439impl ProjectContainer {
440 #[turbo_tasks::function]
441 pub async fn project(&self) -> Result<Vc<Project>> {
442 let env_map: Vc<EnvMap>;
443 let next_config;
444 let define_env;
445 let js_config;
446 let root_path;
447 let project_path;
448 let watch;
449 let dev;
450 let encryption_key;
451 let build_id;
452 let preview_props;
453 let browserslist_query;
454 let no_mangling;
455 let current_node_js_version;
456 {
457 let options = self.options_state.get();
458 let options = options
459 .as_ref()
460 .context("ProjectContainer need to be initialized with initialize()")?;
461 env_map = Vc::cell(options.env.iter().cloned().collect());
462 define_env = ProjectDefineEnv {
463 client: ResolvedVc::cell(options.define_env.client.iter().cloned().collect()),
464 edge: ResolvedVc::cell(options.define_env.edge.iter().cloned().collect()),
465 nodejs: ResolvedVc::cell(options.define_env.nodejs.iter().cloned().collect()),
466 }
467 .cell();
468 next_config = NextConfig::from_string(Vc::cell(options.next_config.clone()));
469 js_config = JsConfig::from_string(Vc::cell(options.js_config.clone()));
470 root_path = options.root_path.clone();
471 project_path = options.project_path.clone();
472 watch = options.watch;
473 dev = options.dev;
474 encryption_key = options.encryption_key.clone();
475 build_id = options.build_id.clone();
476 preview_props = options.preview_props.clone();
477 browserslist_query = options.browserslist_query.clone();
478 no_mangling = options.no_mangling;
479 current_node_js_version = options.current_node_js_version.clone();
480 }
481
482 let dist_dir = next_config
483 .await?
484 .dist_dir
485 .as_ref()
486 .map_or_else(|| rcstr!(".next"), |d| d.clone());
487
488 Ok(Project {
489 root_path,
490 project_path,
491 watch,
492 next_config: next_config.to_resolved().await?,
493 js_config: js_config.to_resolved().await?,
494 dist_dir,
495 env: ResolvedVc::upcast(env_map.to_resolved().await?),
496 define_env: define_env.to_resolved().await?,
497 browserslist_query,
498 mode: if dev {
499 NextMode::Development.resolved_cell()
500 } else {
501 NextMode::Build.resolved_cell()
502 },
503 versioned_content_map: self.versioned_content_map,
504 build_id,
505 encryption_key,
506 preview_props,
507 no_mangling,
508 current_node_js_version,
509 }
510 .cell())
511 }
512
513 #[turbo_tasks::function]
515 pub fn entrypoints(self: Vc<Self>) -> Vc<Entrypoints> {
516 self.project().entrypoints()
517 }
518
519 #[turbo_tasks::function]
521 pub fn hmr_identifiers(self: Vc<Self>) -> Vc<Vec<RcStr>> {
522 self.project().hmr_identifiers()
523 }
524
525 #[turbo_tasks::function]
528 pub fn get_source_map(
529 &self,
530 file_path: FileSystemPath,
531 section: Option<RcStr>,
532 ) -> Vc<OptionStringifiedSourceMap> {
533 if let Some(map) = self.versioned_content_map {
534 map.get_source_map(file_path, section)
535 } else {
536 OptionStringifiedSourceMap::none()
537 }
538 }
539}
540
541#[turbo_tasks::value]
542pub struct Project {
543 root_path: RcStr,
547
548 project_path: RcStr,
552
553 dist_dir: RcStr,
557
558 watch: WatchOptions,
560
561 next_config: ResolvedVc<NextConfig>,
563
564 js_config: ResolvedVc<JsConfig>,
566
567 env: ResolvedVc<Box<dyn ProcessEnv>>,
569
570 define_env: ResolvedVc<ProjectDefineEnv>,
573
574 browserslist_query: RcStr,
576
577 mode: ResolvedVc<NextMode>,
578
579 versioned_content_map: Option<ResolvedVc<VersionedContentMap>>,
580
581 build_id: RcStr,
582
583 encryption_key: RcStr,
584
585 preview_props: DraftModeOptions,
586
587 no_mangling: bool,
591
592 current_node_js_version: RcStr,
593}
594
595#[turbo_tasks::value]
596pub struct ProjectDefineEnv {
597 client: ResolvedVc<OptionEnvMap>,
598 edge: ResolvedVc<OptionEnvMap>,
599 nodejs: ResolvedVc<OptionEnvMap>,
600}
601
602#[turbo_tasks::value_impl]
603impl ProjectDefineEnv {
604 #[turbo_tasks::function]
605 pub fn client(&self) -> Vc<OptionEnvMap> {
606 *self.client
607 }
608
609 #[turbo_tasks::function]
610 pub fn edge(&self) -> Vc<OptionEnvMap> {
611 *self.edge
612 }
613
614 #[turbo_tasks::function]
615 pub fn nodejs(&self) -> Vc<OptionEnvMap> {
616 *self.nodejs
617 }
618}
619
620#[turbo_tasks::value(shared)]
621struct ConflictIssue {
622 path: FileSystemPath,
623 title: ResolvedVc<StyledString>,
624 description: ResolvedVc<StyledString>,
625 severity: IssueSeverity,
626}
627
628#[turbo_tasks::value_impl]
629impl Issue for ConflictIssue {
630 #[turbo_tasks::function]
631 fn stage(&self) -> Vc<IssueStage> {
632 IssueStage::AppStructure.cell()
633 }
634
635 fn severity(&self) -> IssueSeverity {
636 self.severity
637 }
638
639 #[turbo_tasks::function]
640 fn file_path(&self) -> Vc<FileSystemPath> {
641 self.path.clone().cell()
642 }
643
644 #[turbo_tasks::function]
645 fn title(&self) -> Vc<StyledString> {
646 *self.title
647 }
648
649 #[turbo_tasks::function]
650 fn description(&self) -> Vc<OptionStyledString> {
651 Vc::cell(Some(self.description))
652 }
653}
654
655#[turbo_tasks::value_impl]
656impl Project {
657 #[turbo_tasks::function]
658 pub async fn app_project(self: Vc<Self>) -> Result<Vc<OptionAppProject>> {
659 let app_dir = find_app_dir(self.project_path().owned().await?).await?;
660
661 Ok(match &*app_dir {
662 Some(app_dir) => Vc::cell(Some(
663 AppProject::new(self, app_dir.clone()).to_resolved().await?,
664 )),
665 None => Vc::cell(None),
666 })
667 }
668
669 #[turbo_tasks::function]
670 pub fn pages_project(self: Vc<Self>) -> Vc<PagesProject> {
671 PagesProject::new(self)
672 }
673
674 #[turbo_tasks::function]
675 pub fn project_fs(&self) -> Vc<DiskFileSystem> {
676 DiskFileSystem::new(PROJECT_FILESYSTEM_NAME.into(), self.root_path.clone())
677 }
678
679 #[turbo_tasks::function]
680 pub fn client_fs(self: Vc<Self>) -> Vc<Box<dyn FileSystem>> {
681 let virtual_fs = VirtualFileSystem::new_with_name(rcstr!("client-fs"));
682 Vc::upcast(virtual_fs)
683 }
684
685 #[turbo_tasks::function]
686 pub fn output_fs(&self) -> Vc<DiskFileSystem> {
687 DiskFileSystem::new(rcstr!("output"), self.root_path.clone())
688 }
689
690 #[turbo_tasks::function]
691 pub fn dist_dir_absolute(&self) -> Result<Vc<RcStr>> {
692 Ok(Vc::cell(
693 format!(
694 "{}{}{}",
695 self.root_path,
696 std::path::MAIN_SEPARATOR,
697 unix_to_sys(
698 &join_path(&self.project_path, &self.dist_dir)
699 .context("expected project_path to be inside of root_path")?
700 )
701 )
702 .into(),
703 ))
704 }
705
706 #[turbo_tasks::function]
707 pub async fn node_root(self: Vc<Self>) -> Result<Vc<FileSystemPath>> {
708 let this = self.await?;
709 Ok(self
710 .output_fs()
711 .root()
712 .await?
713 .join(&this.project_path)?
714 .join(&this.dist_dir)?
715 .cell())
716 }
717
718 #[turbo_tasks::function]
719 pub fn client_root(self: Vc<Self>) -> Vc<FileSystemPath> {
720 self.client_fs().root()
721 }
722
723 #[turbo_tasks::function]
724 pub fn project_root_path(self: Vc<Self>) -> Vc<FileSystemPath> {
725 self.project_fs().root()
726 }
727
728 #[turbo_tasks::function]
729 pub async fn client_relative_path(self: Vc<Self>) -> Result<Vc<FileSystemPath>> {
730 let next_config = self.next_config().await?;
731 Ok(self
732 .client_root()
733 .await?
734 .join(&format!(
735 "{}/_next",
736 next_config.base_path.clone().unwrap_or_default(),
737 ))?
738 .cell())
739 }
740
741 #[turbo_tasks::function]
745 pub async fn node_root_to_root_path(self: Vc<Self>) -> Result<Vc<RcStr>> {
746 Ok(Vc::cell(
747 self.node_root()
748 .await?
749 .get_relative_path_to(&*self.output_fs().root().await?)
750 .context("Expected node root to be inside of output fs")?,
751 ))
752 }
753
754 #[turbo_tasks::function]
755 pub async fn project_path(self: Vc<Self>) -> Result<Vc<FileSystemPath>> {
756 let this = self.await?;
757 let root = self.project_root_path().await?;
758 Ok(root.join(&this.project_path)?.cell())
759 }
760
761 #[turbo_tasks::function]
762 pub(super) fn env(&self) -> Vc<Box<dyn ProcessEnv>> {
763 *self.env
764 }
765
766 #[turbo_tasks::function]
767 pub(super) fn current_node_js_version(&self) -> Vc<NodeJsVersion> {
768 NodeJsVersion::Static(ResolvedVc::cell(self.current_node_js_version.clone())).cell()
769 }
770
771 #[turbo_tasks::function]
772 pub(super) fn next_config(&self) -> Vc<NextConfig> {
773 *self.next_config
774 }
775
776 #[turbo_tasks::function]
777 pub(super) fn next_mode(&self) -> Vc<NextMode> {
778 *self.mode
779 }
780
781 #[turbo_tasks::function]
782 pub(super) fn is_watch_enabled(&self) -> Result<Vc<bool>> {
783 Ok(Vc::cell(self.watch.enable))
784 }
785
786 #[turbo_tasks::function]
787 pub(super) async fn per_page_module_graph(&self) -> Result<Vc<bool>> {
788 Ok(Vc::cell(*self.mode.await? == NextMode::Development))
789 }
790
791 #[turbo_tasks::function]
792 pub(super) fn js_config(&self) -> Vc<JsConfig> {
793 *self.js_config
794 }
795
796 #[turbo_tasks::function]
797 pub(super) fn encryption_key(&self) -> Vc<RcStr> {
798 Vc::cell(self.encryption_key.clone())
799 }
800
801 #[turbo_tasks::function]
802 pub(super) fn no_mangling(&self) -> Vc<bool> {
803 Vc::cell(self.no_mangling)
804 }
805
806 #[turbo_tasks::function]
807 pub(super) async fn should_create_webpack_stats(&self) -> Result<Vc<bool>> {
808 Ok(Vc::cell(
809 self.env.read(rcstr!("TURBOPACK_STATS")).await?.is_some(),
810 ))
811 }
812
813 #[turbo_tasks::function]
814 pub(super) async fn execution_context(self: Vc<Self>) -> Result<Vc<ExecutionContext>> {
815 let node_root = self.node_root().owned().await?;
816 let next_mode = self.next_mode().await?;
817
818 let node_execution_chunking_context = Vc::upcast(
819 NodeJsChunkingContext::builder(
820 self.project_root_path().owned().await?,
821 node_root.clone(),
822 self.node_root_to_root_path().owned().await?,
823 node_root.clone(),
824 node_root.join("build/chunks")?,
825 node_root.join("build/assets")?,
826 node_build_environment().to_resolved().await?,
827 next_mode.runtime_type(),
828 )
829 .source_maps(if *self.next_config().server_source_maps().await? {
830 SourceMapsType::Full
831 } else {
832 SourceMapsType::None
833 })
834 .build(),
835 );
836
837 Ok(ExecutionContext::new(
838 self.project_path().owned().await?,
839 node_execution_chunking_context,
840 self.env(),
841 ))
842 }
843
844 #[turbo_tasks::function]
845 pub(super) fn client_compile_time_info(&self) -> Vc<CompileTimeInfo> {
846 get_client_compile_time_info(self.browserslist_query.clone(), self.define_env.client())
847 }
848
849 #[turbo_tasks::function]
850 pub async fn get_all_endpoints(self: Vc<Self>, app_dir_only: bool) -> Result<Vc<Endpoints>> {
851 let mut endpoints = Vec::new();
852
853 let entrypoints = self.entrypoints().await?;
854
855 endpoints.push(entrypoints.pages_error_endpoint);
858 endpoints.push(entrypoints.pages_app_endpoint);
859 endpoints.push(entrypoints.pages_document_endpoint);
860
861 if let Some(middleware) = &entrypoints.middleware {
862 endpoints.push(middleware.endpoint);
863 }
864
865 if let Some(instrumentation) = &entrypoints.instrumentation {
866 endpoints.push(instrumentation.node_js);
867 endpoints.push(instrumentation.edge);
868 }
869
870 for (_, route) in entrypoints.routes.iter() {
871 match route {
872 Route::Page {
873 html_endpoint,
874 data_endpoint: _,
875 } => {
876 if !app_dir_only {
877 endpoints.push(*html_endpoint);
878 }
879 }
880 Route::PageApi { endpoint } => {
881 if !app_dir_only {
882 endpoints.push(*endpoint);
883 }
884 }
885 Route::AppPage(page_routes) => {
886 for AppPageRoute {
887 original_name: _,
888 html_endpoint,
889 rsc_endpoint: _,
890 } in page_routes
891 {
892 endpoints.push(*html_endpoint);
893 }
894 }
895 Route::AppRoute {
896 original_name: _,
897 endpoint,
898 } => {
899 endpoints.push(*endpoint);
900 }
901 Route::Conflict => {
902 tracing::info!("WARN: conflict");
903 }
904 }
905 }
906
907 Ok(Vc::cell(endpoints))
908 }
909
910 #[turbo_tasks::function]
911 pub async fn get_all_entries(self: Vc<Self>) -> Result<Vc<GraphEntries>> {
912 let mut modules = self
913 .get_all_endpoints(false)
914 .await?
915 .iter()
916 .map(async |endpoint| Ok(endpoint.entries().owned().await?))
917 .try_flat_join()
918 .await?;
919 modules.extend(self.client_main_modules().await?.iter().cloned());
920 Ok(Vc::cell(modules))
921 }
922
923 #[turbo_tasks::function]
924 pub async fn get_all_additional_entries(
925 self: Vc<Self>,
926 graphs: Vc<ModuleGraph>,
927 ) -> Result<Vc<GraphEntries>> {
928 let modules = self
929 .get_all_endpoints(false)
930 .await?
931 .iter()
932 .map(async |endpoint| Ok(endpoint.additional_entries(graphs).owned().await?))
933 .try_flat_join()
934 .await?;
935 Ok(Vc::cell(modules))
936 }
937
938 #[turbo_tasks::function]
939 pub async fn module_graph(
940 self: Vc<Self>,
941 entry: ResolvedVc<Box<dyn Module>>,
942 ) -> Result<Vc<ModuleGraph>> {
943 Ok(if *self.per_page_module_graph().await? {
944 ModuleGraph::from_entry_module(*entry, self.next_mode().await?.is_production())
945 } else {
946 *self.whole_app_module_graphs().await?.full
947 })
948 }
949
950 #[turbo_tasks::function]
951 pub async fn module_graph_for_modules(
952 self: Vc<Self>,
953 evaluatable_assets: Vc<EvaluatableAssets>,
954 ) -> Result<Vc<ModuleGraph>> {
955 Ok(if *self.per_page_module_graph().await? {
956 let entries = evaluatable_assets
957 .await?
958 .iter()
959 .copied()
960 .map(ResolvedVc::upcast)
961 .collect();
962 ModuleGraph::from_modules(
963 Vc::cell(vec![ChunkGroupEntry::Entry(entries)]),
964 self.next_mode().await?.is_production(),
965 )
966 } else {
967 *self.whole_app_module_graphs().await?.full
968 })
969 }
970
971 #[turbo_tasks::function]
972 pub async fn whole_app_module_graphs(self: ResolvedVc<Self>) -> Result<Vc<ModuleGraphs>> {
973 async move {
974 let module_graphs_op = whole_app_module_graph_operation(self);
975 let module_graphs_vc = module_graphs_op.resolve_strongly_consistent().await?;
976 let _ = module_graphs_op.take_issues_with_path().await?;
977
978 if *self.is_watch_enabled().await? {
981 turbopack_node::evaluate::scale_down();
982 } else {
983 turbopack_node::evaluate::scale_zero();
984 }
985
986 Ok(*module_graphs_vc)
987 }
988 .instrument(tracing::info_span!("module graph for app"))
989 .await
990 }
991
992 #[turbo_tasks::function]
993 pub(super) async fn server_compile_time_info(self: Vc<Self>) -> Result<Vc<CompileTimeInfo>> {
994 let this = self.await?;
995 Ok(get_server_compile_time_info(
996 format!("/ROOT/{}", self.project_path().await?.path).into(),
998 this.define_env.nodejs(),
999 self.current_node_js_version(),
1000 ))
1001 }
1002
1003 #[turbo_tasks::function]
1004 pub(super) async fn edge_compile_time_info(self: Vc<Self>) -> Result<Vc<CompileTimeInfo>> {
1005 let this = self.await?;
1006 Ok(get_edge_compile_time_info(
1007 self.project_path().owned().await?,
1008 this.define_env.edge(),
1009 self.current_node_js_version(),
1010 ))
1011 }
1012
1013 #[turbo_tasks::function]
1014 pub(super) fn edge_env(&self) -> Vc<EnvMap> {
1015 let edge_env = fxindexmap! {
1016 rcstr!("__NEXT_BUILD_ID") => self.build_id.clone(),
1017 rcstr!("NEXT_SERVER_ACTIONS_ENCRYPTION_KEY") => self.encryption_key.clone(),
1018 rcstr!("__NEXT_PREVIEW_MODE_ID") => self.preview_props.preview_mode_id.clone(),
1019 rcstr!("__NEXT_PREVIEW_MODE_ENCRYPTION_KEY") => self.preview_props.preview_mode_encryption_key.clone(),
1020 rcstr!("__NEXT_PREVIEW_MODE_SIGNING_KEY") => self.preview_props.preview_mode_signing_key.clone(),
1021 };
1022 Vc::cell(edge_env)
1023 }
1024
1025 #[turbo_tasks::function]
1026 pub(super) async fn client_chunking_context(
1027 self: Vc<Self>,
1028 ) -> Result<Vc<Box<dyn ChunkingContext>>> {
1029 Ok(get_client_chunking_context(ClientChunkingContextOptions {
1030 mode: self.next_mode(),
1031 root_path: self.project_root_path().owned().await?,
1032 client_root: self.client_relative_path().owned().await?,
1033 client_root_to_root_path: rcstr!("/ROOT"),
1034 asset_prefix: self.next_config().computed_asset_prefix(),
1035 chunk_suffix_path: self.next_config().chunk_suffix_path(),
1036 environment: self.client_compile_time_info().environment(),
1037 module_id_strategy: self.module_ids(),
1038 export_usage: self.export_usage(),
1039 minify: self.next_config().turbo_minify(self.next_mode()),
1040 source_maps: self.next_config().client_source_maps(self.next_mode()),
1041 no_mangling: self.no_mangling(),
1042 scope_hoisting: self.next_config().turbo_scope_hoisting(self.next_mode()),
1043 }))
1044 }
1045
1046 #[turbo_tasks::function]
1047 pub(super) async fn server_chunking_context(
1048 self: Vc<Self>,
1049 client_assets: bool,
1050 ) -> Result<Vc<NodeJsChunkingContext>> {
1051 let options = ServerChunkingContextOptions {
1052 mode: self.next_mode(),
1053 root_path: self.project_root_path().owned().await?,
1054 node_root: self.node_root().owned().await?,
1055 node_root_to_root_path: self.node_root_to_root_path().owned().await?,
1056 environment: self.server_compile_time_info().environment(),
1057 module_id_strategy: self.module_ids(),
1058 export_usage: self.export_usage(),
1059 turbo_minify: self.next_config().turbo_minify(self.next_mode()),
1060 turbo_source_maps: self.next_config().server_source_maps(),
1061 no_mangling: self.no_mangling(),
1062 scope_hoisting: self.next_config().turbo_scope_hoisting(self.next_mode()),
1063 };
1064 Ok(if client_assets {
1065 get_server_chunking_context_with_client_assets(
1066 options,
1067 self.client_relative_path().owned().await?,
1068 self.next_config().computed_asset_prefix().owned().await?,
1069 )
1070 } else {
1071 get_server_chunking_context(options)
1072 })
1073 }
1074
1075 #[turbo_tasks::function]
1076 pub(super) async fn edge_chunking_context(
1077 self: Vc<Self>,
1078 client_assets: bool,
1079 ) -> Result<Vc<Box<dyn ChunkingContext>>> {
1080 let options = EdgeChunkingContextOptions {
1081 mode: self.next_mode(),
1082 root_path: self.project_root_path().owned().await?,
1083 node_root: self.node_root().owned().await?,
1084 output_root_to_root_path: self.node_root_to_root_path(),
1085 environment: self.edge_compile_time_info().environment(),
1086 module_id_strategy: self.module_ids(),
1087 export_usage: self.export_usage(),
1088 turbo_minify: self.next_config().turbo_minify(self.next_mode()),
1089 turbo_source_maps: self.next_config().server_source_maps(),
1090 no_mangling: self.no_mangling(),
1091 scope_hoisting: self.next_config().turbo_scope_hoisting(self.next_mode()),
1092 };
1093 Ok(if client_assets {
1094 get_edge_chunking_context_with_client_assets(
1095 options,
1096 self.client_relative_path().owned().await?,
1097 self.next_config().computed_asset_prefix(),
1098 )
1099 } else {
1100 get_edge_chunking_context(options)
1101 })
1102 }
1103
1104 #[turbo_tasks::function]
1105 pub(super) fn runtime_chunking_context(
1106 self: Vc<Self>,
1107 client_assets: bool,
1108 runtime: NextRuntime,
1109 ) -> Vc<Box<dyn ChunkingContext>> {
1110 match runtime {
1111 NextRuntime::Edge => self.edge_chunking_context(client_assets),
1112 NextRuntime::NodeJs => Vc::upcast(self.server_chunking_context(client_assets)),
1113 }
1114 }
1115
1116 #[turbo_tasks::function]
1119 async fn collect_project_feature_telemetry(self: Vc<Self>) -> Result<Vc<()>> {
1120 let emit_event = |feature_name: &str, enabled: bool| {
1121 NextFeatureTelemetry::new(feature_name.into(), enabled)
1122 .resolved_cell()
1123 .emit();
1124 };
1125
1126 emit_event(env!("VERGEN_CARGO_TARGET_TRIPLE"), true);
1130
1131 let compiler_options = self.js_config().compiler_options().await?;
1133 let compiler_options = compiler_options.as_object();
1134 let experimental_decorators_enabled = compiler_options
1135 .as_ref()
1136 .and_then(|compiler_options| compiler_options.get("experimentalDecorators"))
1137 .is_some();
1138 let jsx_import_source_enabled = compiler_options
1139 .as_ref()
1140 .and_then(|compiler_options| compiler_options.get("jsxImportSource"))
1141 .is_some();
1142
1143 emit_event("swcExperimentalDecorators", experimental_decorators_enabled);
1144 emit_event("swcImportSource", jsx_import_source_enabled);
1145
1146 let config = self.next_config();
1150
1151 emit_event(
1152 "skipMiddlewareUrlNormalize",
1153 *config.skip_middleware_url_normalize().await?,
1154 );
1155
1156 emit_event(
1157 "skipTrailingSlashRedirect",
1158 *config.skip_trailing_slash_redirect().await?,
1159 );
1160 emit_event(
1161 "persistentCaching",
1162 *config.persistent_caching_enabled().await?,
1163 );
1164
1165 let config = &config.await?;
1166
1167 emit_event("modularizeImports", config.modularize_imports.is_some());
1168 emit_event("transpilePackages", config.transpile_packages.is_some());
1169 emit_event("turbotrace", false);
1170
1171 let compiler_options = config.compiler.as_ref();
1173 let swc_relay_enabled = compiler_options.and_then(|c| c.relay.as_ref()).is_some();
1174 let styled_components_enabled = compiler_options
1175 .and_then(|c| c.styled_components.as_ref().map(|sc| sc.is_enabled()))
1176 .unwrap_or_default();
1177 let react_remove_properties_enabled = compiler_options
1178 .and_then(|c| c.react_remove_properties.as_ref().map(|rc| rc.is_enabled()))
1179 .unwrap_or_default();
1180 let remove_console_enabled = compiler_options
1181 .and_then(|c| c.remove_console.as_ref().map(|rc| rc.is_enabled()))
1182 .unwrap_or_default();
1183 let emotion_enabled = compiler_options
1184 .and_then(|c| c.emotion.as_ref().map(|e| e.is_enabled()))
1185 .unwrap_or_default();
1186
1187 emit_event("swcRelay", swc_relay_enabled);
1188 emit_event("swcStyledComponents", styled_components_enabled);
1189 emit_event("swcReactRemoveProperties", react_remove_properties_enabled);
1190 emit_event("swcRemoveConsole", remove_console_enabled);
1191 emit_event("swcEmotion", emotion_enabled);
1192
1193 Ok(Default::default())
1194 }
1195
1196 #[turbo_tasks::function]
1199 pub async fn entrypoints(self: Vc<Self>) -> Result<Vc<Entrypoints>> {
1200 self.collect_project_feature_telemetry().await?;
1201
1202 let mut routes = FxIndexMap::default();
1203 let app_project = self.app_project();
1204 let pages_project = self.pages_project();
1205
1206 if let Some(app_project) = &*app_project.await? {
1207 let app_routes = app_project.routes();
1208 routes.extend(
1209 app_routes
1210 .await?
1211 .iter()
1212 .map(|(k, v)| (k.clone(), v.clone())),
1213 );
1214 }
1215
1216 for (pathname, page_route) in pages_project.routes().await?.iter() {
1217 match routes.entry(pathname.clone()) {
1218 Entry::Occupied(mut entry) => {
1219 ConflictIssue {
1220 path: self.project_path().owned().await?,
1221 title: StyledString::Text(
1222 format!("App Router and Pages Router both match path: {pathname}")
1223 .into(),
1224 )
1225 .resolved_cell(),
1226 description: StyledString::Text(
1227 "Next.js does not support having both App Router and Pages Router \
1228 routes matching the same path. Please remove one of the conflicting \
1229 routes."
1230 .into(),
1231 )
1232 .resolved_cell(),
1233 severity: IssueSeverity::Error,
1234 }
1235 .resolved_cell()
1236 .emit();
1237 *entry.get_mut() = Route::Conflict;
1238 }
1239 Entry::Vacant(entry) => {
1240 entry.insert(page_route.clone());
1241 }
1242 }
1243 }
1244
1245 let pages_document_endpoint = self
1246 .pages_project()
1247 .document_endpoint()
1248 .to_resolved()
1249 .await?;
1250 let pages_app_endpoint = self.pages_project().app_endpoint().to_resolved().await?;
1251 let pages_error_endpoint = self.pages_project().error_endpoint().to_resolved().await?;
1252
1253 let middleware = self.find_middleware();
1254 let middleware = if let FindContextFileResult::Found(..) = *middleware.await? {
1255 Some(Middleware {
1256 endpoint: self.middleware_endpoint().to_resolved().await?,
1257 })
1258 } else {
1259 None
1260 };
1261
1262 let instrumentation = self.find_instrumentation();
1263 let instrumentation = if let FindContextFileResult::Found(..) = *instrumentation.await? {
1264 Some(Instrumentation {
1265 node_js: self.instrumentation_endpoint(false).to_resolved().await?,
1266 edge: self.instrumentation_endpoint(true).to_resolved().await?,
1267 })
1268 } else {
1269 None
1270 };
1271
1272 Ok(Entrypoints {
1273 routes,
1274 middleware,
1275 instrumentation,
1276 pages_document_endpoint,
1277 pages_app_endpoint,
1278 pages_error_endpoint,
1279 }
1280 .cell())
1281 }
1282
1283 #[turbo_tasks::function]
1284 async fn edge_middleware_context(self: Vc<Self>) -> Result<Vc<Box<dyn AssetContext>>> {
1285 let mut transitions = vec![];
1286
1287 let app_dir = find_app_dir(self.project_path().owned().await?)
1288 .owned()
1289 .await?;
1290 let app_project = *self.app_project().await?;
1291
1292 let ecmascript_client_reference_transition_name =
1293 app_project.map(|_| AppProject::client_transition_name());
1294
1295 if let Some(app_project) = app_project {
1296 transitions.push((
1297 AppProject::client_transition_name(),
1298 app_project
1299 .edge_ecmascript_client_reference_transition()
1300 .to_resolved()
1301 .await?,
1302 ));
1303 }
1304
1305 Ok(Vc::upcast(ModuleAssetContext::new(
1306 TransitionOptions {
1307 named_transitions: transitions.clone().into_iter().collect(),
1308 ..Default::default()
1309 }
1310 .cell(),
1311 self.edge_compile_time_info(),
1312 get_server_module_options_context(
1313 self.project_path().owned().await?,
1314 self.execution_context(),
1315 ServerContextType::Middleware {
1316 app_dir: app_dir.clone(),
1317 ecmascript_client_reference_transition_name:
1318 ecmascript_client_reference_transition_name.clone(),
1319 },
1320 self.next_mode(),
1321 self.next_config(),
1322 NextRuntime::Edge,
1323 self.encryption_key(),
1324 self.edge_compile_time_info().environment(),
1325 ),
1326 get_edge_resolve_options_context(
1327 self.project_path().owned().await?,
1328 ServerContextType::Middleware {
1329 app_dir: app_dir.clone(),
1330 ecmascript_client_reference_transition_name:
1331 ecmascript_client_reference_transition_name.clone(),
1332 },
1333 self.next_mode(),
1334 self.next_config(),
1335 self.execution_context(),
1336 None, ),
1338 Layer::new_with_user_friendly_name(
1339 rcstr!("middleware-edge"),
1340 rcstr!("Edge Middleware"),
1341 ),
1342 )))
1343 }
1344
1345 #[turbo_tasks::function]
1346 async fn node_middleware_context(self: Vc<Self>) -> Result<Vc<Box<dyn AssetContext>>> {
1347 let mut transitions = vec![];
1348
1349 let app_dir = find_app_dir(self.project_path().owned().await?)
1350 .owned()
1351 .await?;
1352 let app_project = *self.app_project().await?;
1353
1354 let ecmascript_client_reference_transition_name =
1355 app_project.map(|_| AppProject::client_transition_name());
1356
1357 if let Some(app_project) = app_project {
1358 transitions.push((
1359 AppProject::client_transition_name(),
1360 app_project
1361 .edge_ecmascript_client_reference_transition()
1362 .to_resolved()
1363 .await?,
1364 ));
1365 }
1366
1367 Ok(Vc::upcast(ModuleAssetContext::new(
1368 TransitionOptions {
1369 named_transitions: transitions.clone().into_iter().collect(),
1370 ..Default::default()
1371 }
1372 .cell(),
1373 self.server_compile_time_info(),
1374 get_server_module_options_context(
1375 self.project_path().owned().await?,
1376 self.execution_context(),
1377 ServerContextType::Middleware {
1378 app_dir: app_dir.clone(),
1379 ecmascript_client_reference_transition_name:
1380 ecmascript_client_reference_transition_name.clone(),
1381 },
1382 self.next_mode(),
1383 self.next_config(),
1384 NextRuntime::NodeJs,
1385 self.encryption_key(),
1386 self.server_compile_time_info().environment(),
1387 ),
1388 get_server_resolve_options_context(
1389 self.project_path().owned().await?,
1390 ServerContextType::Middleware {
1391 app_dir: app_dir.clone(),
1392 ecmascript_client_reference_transition_name,
1393 },
1394 self.next_mode(),
1395 self.next_config(),
1396 self.execution_context(),
1397 None, ),
1399 Layer::new_with_user_friendly_name(rcstr!("middleware"), rcstr!("Middleware")),
1400 )))
1401 }
1402
1403 #[turbo_tasks::function]
1404 async fn middleware_context(self: Vc<Self>) -> Result<Vc<Box<dyn AssetContext>>> {
1405 let edge_module_context = self.edge_middleware_context();
1406
1407 let middleware = self.find_middleware();
1408 let FindContextFileResult::Found(fs_path, _) = &*middleware.await? else {
1409 return Ok(Vc::upcast(edge_module_context));
1410 };
1411 let source = Vc::upcast(FileSource::new(fs_path.clone()));
1412
1413 let module = edge_module_context
1414 .process(
1415 source,
1416 ReferenceType::Entry(EntryReferenceSubType::Middleware),
1417 )
1418 .module();
1419
1420 let config = parse_config_from_source(source, module, NextRuntime::Edge).await?;
1421
1422 if matches!(config.runtime, NextRuntime::NodeJs) {
1423 Ok(self.node_middleware_context())
1424 } else {
1425 Ok(edge_module_context)
1426 }
1427 }
1428
1429 #[turbo_tasks::function]
1430 async fn find_middleware(self: Vc<Self>) -> Result<Vc<FindContextFileResult>> {
1431 Ok(find_context_file(
1432 self.project_path().owned().await?,
1433 middleware_files(self.next_config().page_extensions()),
1434 ))
1435 }
1436
1437 #[turbo_tasks::function]
1438 async fn middleware_endpoint(self: Vc<Self>) -> Result<Vc<Box<dyn Endpoint>>> {
1439 let middleware = self.find_middleware();
1440 let FindContextFileResult::Found(fs_path, _) = &*middleware.await? else {
1441 return Ok(Vc::upcast(EmptyEndpoint::new()));
1442 };
1443 let source = Vc::upcast(FileSource::new(fs_path.clone()));
1444 let app_dir = find_app_dir(self.project_path().owned().await?)
1445 .owned()
1446 .await?;
1447 let ecmascript_client_reference_transition_name = (*self.app_project().await?)
1448 .as_ref()
1449 .map(|_| AppProject::client_transition_name());
1450
1451 let middleware_asset_context = self.middleware_context();
1452
1453 Ok(Vc::upcast(MiddlewareEndpoint::new(
1454 self,
1455 middleware_asset_context,
1456 source,
1457 app_dir.clone(),
1458 ecmascript_client_reference_transition_name,
1459 )))
1460 }
1461
1462 #[turbo_tasks::function]
1463 async fn node_instrumentation_context(self: Vc<Self>) -> Result<Vc<Box<dyn AssetContext>>> {
1464 let mut transitions = vec![];
1465
1466 let app_dir = find_app_dir(self.project_path().owned().await?)
1467 .owned()
1468 .await?;
1469 let app_project = &*self.app_project().await?;
1470
1471 let ecmascript_client_reference_transition_name = app_project
1472 .as_ref()
1473 .map(|_| AppProject::client_transition_name());
1474
1475 if let Some(app_project) = app_project {
1476 transitions.push((
1477 AppProject::client_transition_name(),
1478 app_project
1479 .ecmascript_client_reference_transition()
1480 .to_resolved()
1481 .await?,
1482 ));
1483 }
1484
1485 Ok(Vc::upcast(ModuleAssetContext::new(
1486 TransitionOptions {
1487 named_transitions: transitions.into_iter().collect(),
1488 ..Default::default()
1489 }
1490 .cell(),
1491 self.server_compile_time_info(),
1492 get_server_module_options_context(
1493 self.project_path().owned().await?,
1494 self.execution_context(),
1495 ServerContextType::Instrumentation {
1496 app_dir: app_dir.clone(),
1497 ecmascript_client_reference_transition_name:
1498 ecmascript_client_reference_transition_name.clone(),
1499 },
1500 self.next_mode(),
1501 self.next_config(),
1502 NextRuntime::NodeJs,
1503 self.encryption_key(),
1504 self.server_compile_time_info().environment(),
1505 ),
1506 get_server_resolve_options_context(
1507 self.project_path().owned().await?,
1508 ServerContextType::Instrumentation {
1509 app_dir: app_dir.clone(),
1510 ecmascript_client_reference_transition_name,
1511 },
1512 self.next_mode(),
1513 self.next_config(),
1514 self.execution_context(),
1515 None, ),
1517 Layer::new_with_user_friendly_name(
1518 rcstr!("instrumentation"),
1519 rcstr!("Instrumentation"),
1520 ),
1521 )))
1522 }
1523
1524 #[turbo_tasks::function]
1525 async fn edge_instrumentation_context(self: Vc<Self>) -> Result<Vc<Box<dyn AssetContext>>> {
1526 let mut transitions = vec![];
1527
1528 let app_dir = find_app_dir(self.project_path().owned().await?)
1529 .owned()
1530 .await?;
1531 let app_project = &*self.app_project().await?;
1532
1533 let ecmascript_client_reference_transition_name = app_project
1534 .as_ref()
1535 .map(|_| AppProject::client_transition_name());
1536
1537 if let Some(app_project) = app_project {
1538 transitions.push((
1539 AppProject::client_transition_name(),
1540 app_project
1541 .edge_ecmascript_client_reference_transition()
1542 .to_resolved()
1543 .await?,
1544 ));
1545 }
1546
1547 Ok(Vc::upcast(ModuleAssetContext::new(
1548 TransitionOptions {
1549 named_transitions: transitions.into_iter().collect(),
1550 ..Default::default()
1551 }
1552 .cell(),
1553 self.edge_compile_time_info(),
1554 get_server_module_options_context(
1555 self.project_path().owned().await?,
1556 self.execution_context(),
1557 ServerContextType::Instrumentation {
1558 app_dir: app_dir.clone(),
1559 ecmascript_client_reference_transition_name:
1560 ecmascript_client_reference_transition_name.clone(),
1561 },
1562 self.next_mode(),
1563 self.next_config(),
1564 NextRuntime::Edge,
1565 self.encryption_key(),
1566 self.edge_compile_time_info().environment(),
1567 ),
1568 get_edge_resolve_options_context(
1569 self.project_path().owned().await?,
1570 ServerContextType::Instrumentation {
1571 app_dir: app_dir.clone(),
1572 ecmascript_client_reference_transition_name,
1573 },
1574 self.next_mode(),
1575 self.next_config(),
1576 self.execution_context(),
1577 None, ),
1579 Layer::new_with_user_friendly_name(
1580 rcstr!("instrumentation-edge"),
1581 rcstr!("Edge Instrumentation"),
1582 ),
1583 )))
1584 }
1585
1586 #[turbo_tasks::function]
1587 async fn find_instrumentation(self: Vc<Self>) -> Result<Vc<FindContextFileResult>> {
1588 Ok(find_context_file(
1589 self.project_path().owned().await?,
1590 instrumentation_files(self.next_config().page_extensions()),
1591 ))
1592 }
1593
1594 #[turbo_tasks::function]
1595 async fn instrumentation_endpoint(
1596 self: Vc<Self>,
1597 is_edge: bool,
1598 ) -> Result<Vc<Box<dyn Endpoint>>> {
1599 let instrumentation = self.find_instrumentation();
1600 let FindContextFileResult::Found(fs_path, _) = &*instrumentation.await? else {
1601 return Ok(Vc::upcast(EmptyEndpoint::new()));
1602 };
1603 let source = Vc::upcast(FileSource::new(fs_path.clone()));
1604 let app_dir = find_app_dir(self.project_path().owned().await?)
1605 .owned()
1606 .await?;
1607 let ecmascript_client_reference_transition_name = (*self.app_project().await?)
1608 .as_ref()
1609 .map(|_| AppProject::client_transition_name());
1610
1611 let instrumentation_asset_context = if is_edge {
1612 self.edge_instrumentation_context()
1613 } else {
1614 self.node_instrumentation_context()
1615 };
1616
1617 Ok(Vc::upcast(InstrumentationEndpoint::new(
1618 self,
1619 instrumentation_asset_context,
1620 source,
1621 is_edge,
1622 app_dir.clone(),
1623 ecmascript_client_reference_transition_name,
1624 )))
1625 }
1626
1627 #[turbo_tasks::function]
1628 pub async fn emit_all_output_assets(
1629 self: Vc<Self>,
1630 output_assets: OperationVc<OutputAssets>,
1631 ) -> Result<()> {
1632 let span = tracing::info_span!("emitting");
1633 async move {
1634 let all_output_assets = all_assets_from_entries_operation(output_assets);
1635
1636 let client_relative_path = self.client_relative_path().owned().await?;
1637 let node_root = self.node_root().owned().await?;
1638
1639 if let Some(map) = self.await?.versioned_content_map {
1640 map.insert_output_assets(
1641 all_output_assets,
1642 node_root.clone(),
1643 client_relative_path.clone(),
1644 node_root.clone(),
1645 )
1646 .as_side_effect()
1647 .await?;
1648
1649 Ok(())
1650 } else {
1651 emit_assets(
1652 all_output_assets.connect(),
1653 node_root.clone(),
1654 client_relative_path.clone(),
1655 node_root.clone(),
1656 )
1657 .as_side_effect()
1658 .await?;
1659
1660 Ok(())
1661 }
1662 }
1663 .instrument(span)
1664 .await
1665 }
1666
1667 #[turbo_tasks::function]
1668 async fn hmr_content(self: Vc<Self>, identifier: RcStr) -> Result<Vc<OptionVersionedContent>> {
1669 if let Some(map) = self.await?.versioned_content_map {
1670 let content = map.get(self.client_relative_path().await?.join(&identifier)?);
1671 Ok(content)
1672 } else {
1673 bail!("must be in dev mode to hmr")
1674 }
1675 }
1676
1677 #[turbo_tasks::function]
1678 async fn hmr_version(self: Vc<Self>, identifier: RcStr) -> Result<Vc<Box<dyn Version>>> {
1679 let content = self.hmr_content(identifier).await?;
1680 if let Some(content) = &*content {
1681 Ok(content.version())
1682 } else {
1683 Ok(Vc::upcast(NotFoundVersion::new()))
1684 }
1685 }
1686
1687 #[turbo_tasks::function]
1690 pub async fn hmr_version_state(
1691 self: Vc<Self>,
1692 identifier: RcStr,
1693 session: TransientInstance<()>,
1694 ) -> Result<Vc<VersionState>> {
1695 let version = self.hmr_version(identifier);
1696
1697 let _ = session;
1700
1701 let state = VersionState::new(
1705 version
1706 .into_trait_ref()
1707 .strongly_consistent()
1708 .untracked()
1709 .await?,
1710 )
1711 .await?;
1712 Ok(state)
1713 }
1714
1715 #[turbo_tasks::function]
1718 pub async fn hmr_update(
1719 self: Vc<Self>,
1720 identifier: RcStr,
1721 from: Vc<VersionState>,
1722 ) -> Result<Vc<Update>> {
1723 let from = from.get();
1724 let content = self.hmr_content(identifier).await?;
1725 if let Some(content) = *content {
1726 Ok(content.update(from))
1727 } else {
1728 Ok(Update::Missing.cell())
1729 }
1730 }
1731
1732 #[turbo_tasks::function]
1735 pub async fn hmr_identifiers(self: Vc<Self>) -> Result<Vc<Vec<RcStr>>> {
1736 if let Some(map) = self.await?.versioned_content_map {
1737 Ok(map.keys_in_path(self.client_relative_path().owned().await?))
1738 } else {
1739 bail!("must be in dev mode to hmr")
1740 }
1741 }
1742
1743 #[turbo_tasks::function]
1746 pub async fn server_changed(self: Vc<Self>, roots: Vc<OutputAssets>) -> Result<Vc<Completion>> {
1747 let path = self.node_root().owned().await?;
1748 Ok(any_output_changed(roots, path, true))
1749 }
1750
1751 #[turbo_tasks::function]
1754 pub async fn client_changed(self: Vc<Self>, roots: Vc<OutputAssets>) -> Result<Vc<Completion>> {
1755 let path = self.client_root().owned().await?;
1756 Ok(any_output_changed(roots, path, false))
1757 }
1758
1759 #[turbo_tasks::function]
1760 pub async fn client_main_modules(self: Vc<Self>) -> Result<Vc<GraphEntries>> {
1761 let pages_project = self.pages_project();
1762 let mut modules = vec![ChunkGroupEntry::Entry(vec![
1763 pages_project.client_main_module().to_resolved().await?,
1764 ])];
1765
1766 if let Some(app_project) = *self.app_project().await? {
1767 modules.push(ChunkGroupEntry::Entry(vec![
1768 app_project.client_main_module().to_resolved().await?,
1769 ]));
1770 }
1771
1772 Ok(Vc::cell(modules))
1773 }
1774
1775 #[turbo_tasks::function]
1777 pub async fn module_ids(self: Vc<Self>) -> Result<Vc<Box<dyn ModuleIdStrategy>>> {
1778 let module_id_strategy = *self.next_config().module_ids(self.next_mode()).await?;
1779 match module_id_strategy {
1780 ModuleIdStrategyConfig::Named => Ok(Vc::upcast(DevModuleIdStrategy::new())),
1781 ModuleIdStrategyConfig::Deterministic => {
1782 let module_graphs = self.whole_app_module_graphs().await?;
1783 Ok(Vc::upcast(get_global_module_id_strategy(
1784 *module_graphs.full,
1785 )))
1786 }
1787 }
1788 }
1789
1790 #[turbo_tasks::function]
1792 pub async fn export_usage(self: Vc<Self>) -> Result<Vc<OptionExportUsageInfo>> {
1793 if *self
1794 .next_config()
1795 .turbopack_remove_unused_exports(self.next_mode())
1796 .await?
1797 {
1798 let module_graphs = self.whole_app_module_graphs().await?;
1799 Ok(Vc::cell(Some(
1800 compute_export_usage_info(module_graphs.full)
1801 .resolve_strongly_consistent()
1803 .await?,
1804 )))
1805 } else {
1806 Ok(Vc::cell(None))
1807 }
1808 }
1809}
1810
1811#[turbo_tasks::function(operation)]
1814async fn whole_app_module_graph_operation(
1815 project: ResolvedVc<Project>,
1816) -> Result<Vc<ModuleGraphs>> {
1817 mark_root();
1818
1819 let should_trace = project.next_mode().await?.is_production();
1820 let base_single_module_graph =
1821 SingleModuleGraph::new_with_entries(project.get_all_entries(), should_trace);
1822 let base_visited_modules = VisitedModules::from_graph(base_single_module_graph);
1823
1824 let base = ModuleGraph::from_single_graph(base_single_module_graph);
1825 let additional_entries = project.get_all_additional_entries(base);
1826
1827 let additional_module_graph = SingleModuleGraph::new_with_entries_visited(
1828 additional_entries,
1829 base_visited_modules,
1830 should_trace,
1831 );
1832
1833 let full = ModuleGraph::from_graphs(vec![base_single_module_graph, additional_module_graph]);
1834 Ok(ModuleGraphs {
1835 base: base.to_resolved().await?,
1836 full: full.to_resolved().await?,
1837 }
1838 .cell())
1839}
1840
1841#[turbo_tasks::value(shared)]
1842pub struct ModuleGraphs {
1843 pub base: ResolvedVc<ModuleGraph>,
1844 pub full: ResolvedVc<ModuleGraph>,
1845}
1846
1847#[turbo_tasks::function]
1848async fn any_output_changed(
1849 roots: Vc<OutputAssets>,
1850 path: FileSystemPath,
1851 server: bool,
1852) -> Result<Vc<Completion>> {
1853 let completions = AdjacencyMap::new()
1854 .skip_duplicates()
1855 .visit(roots.await?.iter().copied(), get_referenced_output_assets)
1856 .await
1857 .completed()?
1858 .into_inner()
1859 .into_postorder_topological()
1860 .map(|m| {
1861 let path = path.clone();
1862
1863 async move {
1864 let asset_path = m.path().await?;
1865 if !asset_path.path.ends_with(".map")
1866 && (!server || !asset_path.path.ends_with(".css"))
1867 && asset_path.is_inside_ref(&path)
1868 {
1869 anyhow::Ok(Some(content_changed(*ResolvedVc::upcast(m))))
1870 } else {
1871 Ok(None)
1872 }
1873 }
1874 })
1875 .map(|v| async move {
1876 Ok(match v.await? {
1877 Some(v) => Some(v.to_resolved().await?),
1878 None => None,
1879 })
1880 })
1881 .try_flat_join()
1882 .await?;
1883
1884 Ok(Vc::<Completions>::cell(completions).completed())
1885}
1886
1887async fn get_referenced_output_assets(
1888 parent: ResolvedVc<Box<dyn OutputAsset>>,
1889) -> Result<impl Iterator<Item = ResolvedVc<Box<dyn OutputAsset>>> + Send> {
1890 Ok(parent.references().owned().await?.into_iter())
1891}
1892
1893#[turbo_tasks::function(operation)]
1894fn all_assets_from_entries_operation(
1895 operation: OperationVc<OutputAssets>,
1896) -> Result<Vc<OutputAssets>> {
1897 let assets = operation.connect();
1898 Ok(all_assets_from_entries(assets))
1899}
1900
1901#[turbo_tasks::function]
1902fn stable_endpoint(endpoint: Vc<Box<dyn Endpoint>>) -> Vc<Box<dyn Endpoint>> {
1903 endpoint
1904}