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