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