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