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_app::{AppPage, AppPath},
14 next_client::{
15 ClientChunkingContextOptions, get_client_chunking_context, get_client_compile_time_info,
16 },
17 next_config::{
18 ModuleIds as ModuleIdStrategyConfig, NextConfig, TurbopackPluginRuntimeStrategy,
19 },
20 next_edge::context::EdgeChunkingContextOptions,
21 next_server::{
22 ServerChunkingContextOptions, ServerContextType, get_server_chunking_context,
23 get_server_chunking_context_with_client_assets, get_server_compile_time_info,
24 get_server_module_options_context, get_server_resolve_options_context,
25 },
26 next_telemetry::NextFeatureTelemetry,
27 parse_segment_config_from_source,
28 segment_config::ParseSegmentMode,
29 util::{NextRuntime, OptionEnvMap},
30};
31use rustc_hash::{FxHashMap, FxHashSet};
32use serde::{Deserialize, Serialize};
33use tracing::{Instrument, field::Empty};
34use turbo_rcstr::{RcStr, rcstr};
35use turbo_tasks::{
36 Completion, Completions, FxIndexMap, NonLocalValue, OperationValue, OperationVc, ReadRef,
37 ResolvedVc, State, TaskInput, TransientInstance, TryFlatJoinIterExt, Vc,
38 debug::ValueDebugFormat, fxindexmap, trace::TraceRawVcs,
39};
40use turbo_tasks_env::{EnvMap, ProcessEnv};
41use turbo_tasks_fs::{
42 DiskFileSystem, FileContent, FileSystem, FileSystemPath, VirtualFileSystem, invalidation,
43};
44use turbo_unix_path::{join_path, unix_to_sys};
45use turbopack::{
46 ModuleAssetContext, evaluate_context::node_build_environment,
47 global_module_ids::get_global_module_id_strategy, transition::TransitionOptions,
48};
49use turbopack_core::{
50 PROJECT_FILESYSTEM_NAME,
51 changed::content_changed,
52 chunk::{
53 ChunkingContext, EvaluatableAssets, UnusedReferences,
54 chunk_id_strategy::{ModuleIdFallback, ModuleIdStrategy},
55 },
56 compile_time_info::CompileTimeInfo,
57 context::AssetContext,
58 diagnostics::DiagnosticExt,
59 environment::NodeJsVersion,
60 file_source::FileSource,
61 ident::Layer,
62 issue::{
63 CollectibleIssuesExt, Issue, IssueExt, IssueFilter, IssueSeverity, IssueStage,
64 OptionStyledString, StyledString,
65 },
66 module::Module,
67 module_graph::{
68 GraphEntries, ModuleGraph, SingleModuleGraph, VisitedModules,
69 binding_usage_info::{
70 BindingUsageInfo, OptionBindingUsageInfo, compute_binding_usage_info,
71 },
72 chunk_group_info::ChunkGroupEntry,
73 },
74 output::{
75 ExpandOutputAssetsInput, ExpandedOutputAssets, OutputAsset, OutputAssets,
76 expand_output_assets,
77 },
78 reference::all_assets_from_entries,
79 resolve::{FindContextFileResult, find_context_file},
80 version::{
81 NotFoundVersion, OptionVersionedContent, Update, Version, VersionState, VersionedContent,
82 },
83};
84#[cfg(feature = "process_pool")]
85use turbopack_node::child_process_backend;
86use turbopack_node::execution_context::ExecutionContext;
87#[cfg(feature = "worker_pool")]
88use turbopack_node::worker_threads_backend;
89use turbopack_nodejs::NodeJsChunkingContext;
90
91use crate::{
92 app::{AppProject, OptionAppProject},
93 empty::EmptyEndpoint,
94 entrypoints::Entrypoints,
95 instrumentation::InstrumentationEndpoint,
96 middleware::MiddlewareEndpoint,
97 pages::PagesProject,
98 route::{
99 Endpoint, EndpointGroup, EndpointGroupEntry, EndpointGroupKey, EndpointGroups, Endpoints,
100 Route,
101 },
102 versioned_content_map::VersionedContentMap,
103};
104
105#[derive(
106 Debug,
107 Serialize,
108 Deserialize,
109 Clone,
110 TaskInput,
111 PartialEq,
112 Eq,
113 Hash,
114 TraceRawVcs,
115 NonLocalValue,
116 OperationValue,
117 Encode,
118 Decode,
119)]
120#[serde(rename_all = "camelCase")]
121pub struct DraftModeOptions {
122 pub preview_mode_id: RcStr,
123 pub preview_mode_encryption_key: RcStr,
124 pub preview_mode_signing_key: RcStr,
125}
126
127#[derive(
128 Debug,
129 Default,
130 Serialize,
131 Deserialize,
132 Copy,
133 Clone,
134 TaskInput,
135 PartialEq,
136 Eq,
137 Hash,
138 TraceRawVcs,
139 NonLocalValue,
140 OperationValue,
141 Encode,
142 Decode,
143)]
144#[serde(rename_all = "camelCase")]
145pub struct WatchOptions {
146 pub enable: bool,
148
149 pub poll_interval: Option<Duration>,
152}
153
154#[derive(
155 Debug,
156 Default,
157 Serialize,
158 Deserialize,
159 Clone,
160 TaskInput,
161 PartialEq,
162 Eq,
163 Hash,
164 TraceRawVcs,
165 NonLocalValue,
166 OperationValue,
167 Encode,
168 Decode,
169)]
170#[serde(rename_all = "camelCase")]
171pub struct DebugBuildPaths {
172 pub app: Vec<RcStr>,
173 pub pages: Vec<RcStr>,
174}
175
176#[derive(
178 Debug,
179 Default,
180 Copy,
181 Clone,
182 TaskInput,
183 PartialEq,
184 Eq,
185 Hash,
186 TraceRawVcs,
187 NonLocalValue,
188 Encode,
189 Decode,
190)]
191pub enum HmrTarget {
192 #[default]
193 Client,
194 Server,
195}
196
197impl std::fmt::Display for HmrTarget {
198 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199 match self {
200 HmrTarget::Client => write!(f, "client"),
201 HmrTarget::Server => write!(f, "server"),
202 }
203 }
204}
205
206impl std::str::FromStr for HmrTarget {
207 type Err = String;
208
209 fn from_str(s: &str) -> Result<Self, Self::Err> {
210 match s {
211 "client" => Ok(HmrTarget::Client),
212 "server" => Ok(HmrTarget::Server),
213 _ => Err(format!(
214 "Invalid HMR target: '{}'. Expected 'client' or 'server'",
215 s
216 )),
217 }
218 }
219}
220
221struct DebugBuildPathsRouteKeys {
223 app: FxHashSet<RcStr>,
224 pages: FxHashSet<RcStr>,
225}
226
227impl DebugBuildPathsRouteKeys {
228 fn app_route_key_from_debug_path(path: &str) -> Result<RcStr> {
229 let mut segments = path
230 .trim_start_matches('/')
231 .split('/')
232 .filter(|segment| !segment.is_empty())
233 .collect::<Vec<_>>();
234
235 if let Some(last_segment) = segments.last()
236 && (*last_segment == "page"
237 || last_segment.starts_with("page.")
238 || *last_segment == "route"
239 || last_segment.starts_with("route."))
240 {
241 segments.pop();
242 }
243
244 let normalized_path = segments.join("/");
245 Ok(AppPath::from(AppPage::parse(&normalized_path)?)
246 .to_string()
247 .into())
248 }
249
250 fn from_debug_build_paths(paths: &DebugBuildPaths) -> Result<Self> {
251 Ok(Self {
252 app: paths
253 .app
254 .iter()
255 .map(|path| Self::app_route_key_from_debug_path(path))
256 .collect::<Result<FxHashSet<_>>>()?,
257 pages: paths
258 .pages
259 .iter()
260 .map(|path| {
261 let file_name = path.rsplit('/').next().unwrap_or(path);
265 if let Some(dot_idx) = file_name.rfind('.') {
266 let ext = &file_name[dot_idx + 1..];
267 if !ext.is_empty() && ext.chars().all(|c| c.is_ascii_alphanumeric()) {
268 let trimmed_len = path.len() - (file_name.len() - dot_idx);
269 return path[..trimmed_len].into();
270 }
271 }
272 path.clone()
273 })
274 .collect(),
275 })
276 }
277
278 fn should_include_app_route(&self, route_key: &RcStr) -> bool {
279 if matches!(route_key.as_str(), "/_not-found" | "/_global-error") {
281 return true;
282 }
283 self.app.contains(route_key)
284 }
285
286 fn should_include_pages_route(&self, route_key: &RcStr) -> bool {
287 if matches!(route_key.as_str(), "/_error" | "/_document" | "/_app") {
289 return true;
290 }
291 self.pages.contains(route_key)
292 }
293}
294
295#[derive(
296 Debug,
297 Serialize,
298 Deserialize,
299 Clone,
300 PartialEq,
301 Eq,
302 TraceRawVcs,
303 NonLocalValue,
304 OperationValue,
305 Encode,
306 Decode,
307)]
308#[serde(rename_all = "camelCase")]
309pub struct ProjectOptions {
310 pub root_path: RcStr,
314
315 pub project_path: RcStr,
318
319 pub next_config: RcStr,
321
322 pub env: Vec<(RcStr, RcStr)>,
324
325 pub define_env: DefineEnv,
328
329 pub watch: WatchOptions,
331
332 pub dev: bool,
334
335 pub encryption_key: RcStr,
337
338 pub build_id: RcStr,
340
341 pub preview_props: DraftModeOptions,
343
344 pub browserslist_query: RcStr,
346
347 pub no_mangling: bool,
351
352 pub write_routes_hashes_manifest: bool,
354
355 pub current_node_js_version: RcStr,
357
358 pub debug_build_paths: Option<DebugBuildPaths>,
361
362 pub deferred_entries: Option<Vec<RcStr>>,
364
365 pub is_persistent_caching_enabled: bool,
367
368 pub next_version: RcStr,
370
371 pub server_hmr: bool,
373}
374
375#[derive(Default)]
376pub struct PartialProjectOptions {
377 pub root_path: Option<RcStr>,
380
381 pub project_path: Option<RcStr>,
383
384 pub next_config: Option<RcStr>,
386
387 pub env: Option<Vec<(RcStr, RcStr)>>,
389
390 pub define_env: Option<DefineEnv>,
393
394 pub watch: Option<WatchOptions>,
396
397 pub dev: Option<bool>,
399
400 pub encryption_key: Option<RcStr>,
402
403 pub build_id: Option<RcStr>,
405
406 pub preview_props: Option<DraftModeOptions>,
408
409 pub browserslist_query: Option<RcStr>,
411
412 pub no_mangling: Option<bool>,
416
417 pub write_routes_hashes_manifest: Option<bool>,
419
420 pub debug_build_paths: Option<DebugBuildPaths>,
423}
424
425#[derive(
426 Debug,
427 Serialize,
428 Deserialize,
429 Clone,
430 TaskInput,
431 PartialEq,
432 Eq,
433 Hash,
434 TraceRawVcs,
435 NonLocalValue,
436 OperationValue,
437 Encode,
438 Decode,
439)]
440#[serde(rename_all = "camelCase")]
441pub struct DefineEnv {
442 pub client: Vec<(RcStr, Option<RcStr>)>,
443 pub edge: Vec<(RcStr, Option<RcStr>)>,
444 pub nodejs: Vec<(RcStr, Option<RcStr>)>,
445}
446
447#[derive(TraceRawVcs, PartialEq, Eq, ValueDebugFormat, NonLocalValue, Encode, Decode)]
448pub struct Middleware {
449 pub endpoint: ResolvedVc<Box<dyn Endpoint>>,
450 pub is_proxy: bool,
451}
452
453#[derive(TraceRawVcs, PartialEq, Eq, ValueDebugFormat, NonLocalValue, Encode, Decode)]
454pub struct Instrumentation {
455 pub node_js: ResolvedVc<Box<dyn Endpoint>>,
456 pub edge: ResolvedVc<Box<dyn Endpoint>>,
457}
458
459#[turbo_tasks::value]
460pub struct ProjectContainer {
461 name: RcStr,
462 options_state: State<Option<ProjectOptions>>,
463 versioned_content_map: Option<ResolvedVc<VersionedContentMap>>,
464}
465
466#[turbo_tasks::value_impl]
467impl ProjectContainer {
468 #[turbo_tasks::function(operation)]
469 pub fn new_operation(name: RcStr, dev: bool) -> Result<Vc<Self>> {
470 Ok(ProjectContainer {
471 name,
472 versioned_content_map: if dev {
475 Some(VersionedContentMap::new())
476 } else {
477 None
478 },
479 options_state: State::new(None),
480 }
481 .cell())
482 }
483}
484
485#[turbo_tasks::function(operation)]
486fn project_operation(project: ResolvedVc<ProjectContainer>) -> Vc<Project> {
487 project.project()
488}
489
490#[turbo_tasks::function(operation)]
491fn project_fs_operation(project: ResolvedVc<Project>) -> Vc<DiskFileSystem> {
492 project.project_fs()
493}
494
495#[turbo_tasks::function(operation)]
496fn output_fs_operation(project: ResolvedVc<Project>) -> Vc<DiskFileSystem> {
497 project.project_fs()
498}
499
500enum EnvDiffType {
501 Added,
502 Removed,
503 Modified,
504}
505
506fn env_diff(
507 old: &[(RcStr, Option<RcStr>)],
508 new: &[(RcStr, Option<RcStr>)],
509) -> Vec<(RcStr, EnvDiffType)> {
510 let mut diffs = Vec::new();
511 let mut old_map: FxHashMap<_, _> = old.iter().cloned().collect();
512
513 for (key, new_value) in new.iter() {
514 match old_map.remove(key) {
515 Some(old_value) => {
516 if &old_value != new_value {
517 diffs.push((key.clone(), EnvDiffType::Modified));
518 }
519 }
520 None => {
521 diffs.push((key.clone(), EnvDiffType::Added));
522 }
523 }
524 }
525
526 for (key, _) in old.iter() {
527 if old_map.contains_key(key) {
528 diffs.push((key.clone(), EnvDiffType::Removed));
529 }
530 }
531
532 diffs
533}
534
535fn env_diff_report(old: &[(RcStr, Option<RcStr>)], new: &[(RcStr, Option<RcStr>)]) -> String {
536 use std::fmt::Write;
537
538 let diff = env_diff(old, new);
539
540 let mut report = String::new();
541 for (key, diff_type) in diff {
542 let symbol = match diff_type {
543 EnvDiffType::Added => "+",
544 EnvDiffType::Removed => "-",
545 EnvDiffType::Modified => "*",
546 };
547 if !report.is_empty() {
548 report.push_str(", ");
549 }
550 write!(report, "{}{}", symbol, key).unwrap();
551 }
552 report
553}
554
555fn define_env_diff_report(old: &DefineEnv, new: &DefineEnv) -> String {
556 use std::fmt::Write;
557
558 let mut report = String::new();
559 for (name, old, new) in [
560 ("client", &old.client, &new.client),
561 ("edge", &old.edge, &new.edge),
562 ("nodejs", &old.nodejs, &new.nodejs),
563 ] {
564 let diff = env_diff_report(old, new);
565 if !diff.is_empty() {
566 if !report.is_empty() {
567 report.push_str(", ");
568 }
569 write!(report, "{name}: {{ {diff} }}").unwrap();
570 }
571 }
572 report
573}
574
575impl ProjectContainer {
576 pub async fn initialize(this_op: OperationVc<Self>, options: ProjectOptions) -> Result<()> {
584 let this = this_op.read_strongly_consistent().await?;
585 let span = tracing::info_span!(
586 "initialize project",
587 project_name = %this.name,
588 version = options.next_version.as_str(),
589 env_diff = Empty
590 );
591 let span_clone = span.clone();
592 async move {
593 let watch = options.watch;
594
595 if let Some(old_options) = &*this.options_state.get_untracked() {
596 span.record(
597 "env_diff",
598 define_env_diff_report(&old_options.define_env, &options.define_env).as_str(),
599 );
600 }
601 this.options_state.set(Some(options));
602
603 #[turbo_tasks::function(operation)]
604 fn project_from_container_operation(
605 container: OperationVc<ProjectContainer>,
606 ) -> Vc<Project> {
607 container.connect().project()
608 }
609 let project = project_from_container_operation(this_op)
610 .resolve_strongly_consistent()
611 .await?;
612 let project_fs = project_fs_operation(project)
613 .read_strongly_consistent()
614 .await?;
615 if watch.enable {
616 project_fs
617 .start_watching_with_invalidation_reason(watch.poll_interval)
618 .await?;
619 } else {
620 project_fs.invalidate_with_reason(|path| invalidation::Initialize {
621 path: RcStr::from(path.to_string_lossy()),
623 });
624 }
625 let output_fs = output_fs_operation(project)
626 .read_strongly_consistent()
627 .await?;
628 output_fs.invalidate_with_reason(|path| invalidation::Initialize {
629 path: RcStr::from(path.to_string_lossy()),
630 });
631 Ok(())
632 }
633 .instrument(span_clone)
634 .await
635 }
636
637 pub async fn update(self: ResolvedVc<Self>, options: PartialProjectOptions) -> Result<()> {
638 let span = tracing::info_span!(
639 "update project options",
640 project_name = %self.await?.name,
641 env_diff = Empty
642 );
643 let span_clone = span.clone();
644 async move {
645 #[turbo_tasks::function(operation)]
651 fn project_container_operation_hack(
652 container: ResolvedVc<ProjectContainer>,
653 ) -> Vc<ProjectContainer> {
654 *container
655 }
656 let this = project_container_operation_hack(self)
657 .read_strongly_consistent()
658 .await?;
659 let PartialProjectOptions {
660 root_path,
661 project_path,
662 next_config,
663 env,
664 define_env,
665 watch,
666 dev,
667 encryption_key,
668 build_id,
669 preview_props,
670 browserslist_query,
671 no_mangling,
672 write_routes_hashes_manifest,
673 debug_build_paths,
674 } = options;
675
676 let mut new_options = this
677 .options_state
678 .get()
679 .clone()
680 .context("ProjectContainer need to be initialized with initialize()")?;
681
682 if let Some(root_path) = root_path {
683 new_options.root_path = root_path;
684 }
685 if let Some(project_path) = project_path {
686 new_options.project_path = project_path;
687 }
688 if let Some(next_config) = next_config {
689 new_options.next_config = next_config;
690 }
691 if let Some(env) = env {
692 new_options.env = env;
693 }
694 if let Some(define_env) = define_env {
695 new_options.define_env = define_env;
696 }
697 if let Some(watch) = watch {
698 new_options.watch = watch;
699 }
700 if let Some(dev) = dev {
701 new_options.dev = dev;
702 }
703 if let Some(encryption_key) = encryption_key {
704 new_options.encryption_key = encryption_key;
705 }
706 if let Some(build_id) = build_id {
707 new_options.build_id = build_id;
708 }
709 if let Some(preview_props) = preview_props {
710 new_options.preview_props = preview_props;
711 }
712 if let Some(browserslist_query) = browserslist_query {
713 new_options.browserslist_query = browserslist_query;
714 }
715 if let Some(no_mangling) = no_mangling {
716 new_options.no_mangling = no_mangling;
717 }
718 if let Some(write_routes_hashes_manifest) = write_routes_hashes_manifest {
719 new_options.write_routes_hashes_manifest = write_routes_hashes_manifest;
720 }
721 if let Some(debug_build_paths) = debug_build_paths {
722 new_options.debug_build_paths = Some(debug_build_paths);
723 }
724
725 let watch = new_options.watch;
727
728 let project = project_operation(self)
729 .resolve_strongly_consistent()
730 .await?;
731 let prev_project_fs = project_fs_operation(project)
732 .read_strongly_consistent()
733 .await?;
734 let prev_output_fs = output_fs_operation(project)
735 .read_strongly_consistent()
736 .await?;
737
738 if let Some(old_options) = &*this.options_state.get_untracked() {
739 span.record(
740 "env_diff",
741 define_env_diff_report(&old_options.define_env, &new_options.define_env)
742 .as_str(),
743 );
744 }
745 this.options_state.set(Some(new_options));
746 let project = project_operation(self)
747 .resolve_strongly_consistent()
748 .await?;
749 let project_fs = project_fs_operation(project)
750 .read_strongly_consistent()
751 .await?;
752 let output_fs = output_fs_operation(project)
753 .read_strongly_consistent()
754 .await?;
755
756 if !ReadRef::ptr_eq(&prev_project_fs, &project_fs) {
757 if watch.enable {
758 project_fs
760 .start_watching_with_invalidation_reason(watch.poll_interval)
761 .await?;
762 } else {
763 project_fs.invalidate_with_reason(|path| invalidation::Initialize {
764 path: RcStr::from(path.to_string_lossy()),
766 });
767 }
768 }
769 if !ReadRef::ptr_eq(&prev_output_fs, &output_fs) {
770 prev_output_fs.invalidate_with_reason(|path| invalidation::Initialize {
771 path: RcStr::from(path.to_string_lossy()),
772 });
773 }
774
775 Ok(())
776 }
777 .instrument(span_clone)
778 .await
779 }
780}
781
782#[turbo_tasks::value_impl]
783impl ProjectContainer {
784 #[turbo_tasks::function]
785 pub async fn project(&self) -> Result<Vc<Project>> {
786 let env_map: Vc<EnvMap>;
787 let next_config;
788 let define_env;
789 let root_path;
790 let project_path;
791 let watch;
792 let dev;
793 let encryption_key;
794 let build_id;
795 let preview_props;
796 let browserslist_query;
797 let no_mangling;
798 let write_routes_hashes_manifest;
799 let current_node_js_version;
800 let debug_build_paths;
801 let deferred_entries;
802 let is_persistent_caching_enabled;
803 let server_hmr;
804 {
805 let options = self.options_state.get();
806 let options = options
807 .as_ref()
808 .context("ProjectContainer need to be initialized with initialize()")?;
809 env_map = Vc::cell(options.env.iter().cloned().collect());
810 define_env = ProjectDefineEnv {
811 client: ResolvedVc::cell(options.define_env.client.iter().cloned().collect()),
812 edge: ResolvedVc::cell(options.define_env.edge.iter().cloned().collect()),
813 nodejs: ResolvedVc::cell(options.define_env.nodejs.iter().cloned().collect()),
814 }
815 .cell();
816 next_config = NextConfig::from_string(Vc::cell(options.next_config.clone()));
817 root_path = options.root_path.clone();
818 project_path = options.project_path.clone();
819 watch = options.watch;
820 dev = options.dev;
821 encryption_key = options.encryption_key.clone();
822 build_id = options.build_id.clone();
823 preview_props = options.preview_props.clone();
824 browserslist_query = options.browserslist_query.clone();
825 no_mangling = options.no_mangling;
826 write_routes_hashes_manifest = options.write_routes_hashes_manifest;
827 current_node_js_version = options.current_node_js_version.clone();
828 debug_build_paths = options.debug_build_paths.clone();
829 deferred_entries = options.deferred_entries.clone().unwrap_or_default();
830 is_persistent_caching_enabled = options.is_persistent_caching_enabled;
831 server_hmr = options.server_hmr;
832 }
833
834 let dist_dir = next_config.dist_dir().owned().await?;
835 let dist_dir_root = next_config.dist_dir_root().owned().await?;
836 Ok(Project {
837 root_path,
838 project_path,
839 watch,
840 next_config: next_config.to_resolved().await?,
841 dist_dir,
842 dist_dir_root,
843 env: ResolvedVc::upcast(env_map.to_resolved().await?),
844 define_env: define_env.to_resolved().await?,
845 browserslist_query,
846 mode: if dev {
847 NextMode::Development.resolved_cell()
848 } else {
849 NextMode::Build.resolved_cell()
850 },
851 versioned_content_map: self.versioned_content_map,
852 build_id,
853 encryption_key,
854 preview_props,
855 no_mangling,
856 write_routes_hashes_manifest,
857 current_node_js_version,
858 debug_build_paths,
859 deferred_entries,
860 is_persistent_caching_enabled,
861 server_hmr,
862 }
863 .cell())
864 }
865
866 #[turbo_tasks::function]
868 pub fn entrypoints(self: Vc<Self>) -> Vc<Entrypoints> {
869 self.project().entrypoints()
870 }
871
872 #[turbo_tasks::function]
874 pub fn hmr_chunk_names(self: Vc<Self>, target: HmrTarget) -> Vc<Vec<RcStr>> {
875 self.project().hmr_chunk_names(target)
876 }
877
878 #[turbo_tasks::function]
881 pub fn get_source_map(
882 &self,
883 file_path: FileSystemPath,
884 section: Option<RcStr>,
885 ) -> Vc<FileContent> {
886 if let Some(map) = self.versioned_content_map {
887 map.get_source_map(file_path, section)
888 } else {
889 FileContent::NotFound.cell()
890 }
891 }
892}
893
894#[derive(Clone)]
895#[turbo_tasks::value]
896pub struct Project {
897 root_path: RcStr,
901
902 project_path: RcStr,
906
907 dist_dir: RcStr,
911
912 dist_dir_root: RcStr,
916
917 watch: WatchOptions,
919
920 next_config: ResolvedVc<NextConfig>,
922
923 env: ResolvedVc<Box<dyn ProcessEnv>>,
925
926 define_env: ResolvedVc<ProjectDefineEnv>,
929
930 browserslist_query: RcStr,
932
933 mode: ResolvedVc<NextMode>,
934
935 versioned_content_map: Option<ResolvedVc<VersionedContentMap>>,
936
937 build_id: RcStr,
938
939 encryption_key: RcStr,
940
941 preview_props: DraftModeOptions,
942
943 no_mangling: bool,
947
948 write_routes_hashes_manifest: bool,
950
951 current_node_js_version: RcStr,
952
953 debug_build_paths: Option<DebugBuildPaths>,
956
957 deferred_entries: Vec<RcStr>,
959
960 is_persistent_caching_enabled: bool,
962
963 server_hmr: bool,
965}
966
967#[turbo_tasks::value]
968pub struct ProjectDefineEnv {
969 client: ResolvedVc<OptionEnvMap>,
970 edge: ResolvedVc<OptionEnvMap>,
971 nodejs: ResolvedVc<OptionEnvMap>,
972}
973
974#[turbo_tasks::value_impl]
975impl ProjectDefineEnv {
976 #[turbo_tasks::function]
977 pub fn client(&self) -> Vc<OptionEnvMap> {
978 *self.client
979 }
980
981 #[turbo_tasks::function]
982 pub fn edge(&self) -> Vc<OptionEnvMap> {
983 *self.edge
984 }
985
986 #[turbo_tasks::function]
987 pub fn nodejs(&self) -> Vc<OptionEnvMap> {
988 *self.nodejs
989 }
990}
991
992#[turbo_tasks::value(shared)]
993struct ConflictIssue {
994 path: FileSystemPath,
995 title: ResolvedVc<StyledString>,
996 description: ResolvedVc<StyledString>,
997 severity: IssueSeverity,
998}
999
1000#[turbo_tasks::value_impl]
1001impl Issue for ConflictIssue {
1002 #[turbo_tasks::function]
1003 fn stage(&self) -> Vc<IssueStage> {
1004 IssueStage::AppStructure.cell()
1005 }
1006
1007 fn severity(&self) -> IssueSeverity {
1008 self.severity
1009 }
1010
1011 #[turbo_tasks::function]
1012 fn file_path(&self) -> Vc<FileSystemPath> {
1013 self.path.clone().cell()
1014 }
1015
1016 #[turbo_tasks::function]
1017 fn title(&self) -> Vc<StyledString> {
1018 *self.title
1019 }
1020
1021 #[turbo_tasks::function]
1022 fn description(&self) -> Vc<OptionStyledString> {
1023 Vc::cell(Some(self.description))
1024 }
1025}
1026
1027#[turbo_tasks::value_impl]
1028impl Project {
1029 #[turbo_tasks::function]
1030 pub async fn app_project(self: Vc<Self>) -> Result<Vc<OptionAppProject>> {
1031 let app_dir = find_app_dir(self.project_path().owned().await?).await?;
1032
1033 Ok(match &*app_dir {
1034 Some(app_dir) => Vc::cell(Some(
1035 AppProject::new(self, app_dir.clone()).to_resolved().await?,
1036 )),
1037 None => Vc::cell(None),
1038 })
1039 }
1040
1041 #[turbo_tasks::function]
1042 pub fn pages_project(self: Vc<Self>) -> Vc<PagesProject> {
1043 PagesProject::new(self)
1044 }
1045
1046 #[turbo_tasks::function]
1047 pub fn project_fs(&self) -> Result<Vc<DiskFileSystem>> {
1048 let denied_path = match join_path(&self.project_path, &self.dist_dir_root) {
1049 Some(dist_dir_root) => dist_dir_root.into(),
1050 None => {
1051 bail!(
1052 "Invalid distDirRoot: {:?}. distDirRoot should not navigate out of the \
1053 projectPath.",
1054 self.dist_dir_root
1055 );
1056 }
1057 };
1058
1059 Ok(DiskFileSystem::new_with_denied_paths(
1060 rcstr!(PROJECT_FILESYSTEM_NAME),
1061 self.root_path.clone(),
1062 vec![denied_path],
1063 ))
1064 }
1065
1066 #[turbo_tasks::function]
1067 pub fn client_fs(self: Vc<Self>) -> Vc<Box<dyn FileSystem>> {
1068 let virtual_fs = VirtualFileSystem::new_with_name(rcstr!("client-fs"));
1069 Vc::upcast(virtual_fs)
1070 }
1071
1072 #[turbo_tasks::function]
1073 pub fn output_fs(&self) -> Vc<DiskFileSystem> {
1074 DiskFileSystem::new(rcstr!("output"), self.root_path.clone())
1075 }
1076
1077 #[turbo_tasks::function]
1078 pub fn dist_dir_absolute(&self) -> Result<Vc<RcStr>> {
1079 Ok(Vc::cell(
1080 format!(
1081 "{}{}{}",
1082 self.root_path,
1083 std::path::MAIN_SEPARATOR,
1084 unix_to_sys(
1085 &join_path(&self.project_path, &self.dist_dir)
1086 .context("expected project_path to be inside of root_path")?
1087 )
1088 )
1089 .into(),
1090 ))
1091 }
1092
1093 #[turbo_tasks::function]
1094 pub async fn node_root(self: Vc<Self>) -> Result<Vc<FileSystemPath>> {
1095 let this = self.await?;
1096 Ok(self
1097 .output_fs()
1098 .root()
1099 .await?
1100 .join(&this.project_path)?
1101 .join(&this.dist_dir)?
1102 .cell())
1103 }
1104
1105 #[turbo_tasks::function]
1106 pub fn client_root(self: Vc<Self>) -> Vc<FileSystemPath> {
1107 self.client_fs().root()
1108 }
1109
1110 #[turbo_tasks::function]
1111 pub fn project_root_path(self: Vc<Self>) -> Vc<FileSystemPath> {
1112 self.project_fs().root()
1113 }
1114
1115 #[turbo_tasks::function]
1116 pub async fn client_relative_path(self: Vc<Self>) -> Result<Vc<FileSystemPath>> {
1117 let next_config = self.next_config();
1118 Ok(self
1119 .client_root()
1120 .await?
1121 .join(&format!(
1122 "{}/_next",
1123 next_config
1124 .base_path()
1125 .await?
1126 .as_deref()
1127 .unwrap_or_default(),
1128 ))?
1129 .cell())
1130 }
1131
1132 #[turbo_tasks::function]
1136 pub async fn node_root_to_root_path(self: Vc<Self>) -> Result<Vc<RcStr>> {
1137 Ok(Vc::cell(
1138 self.node_root()
1139 .await?
1140 .get_relative_path_to(&*self.output_fs().root().await?)
1141 .context("Expected node root to be inside of output fs")?,
1142 ))
1143 }
1144
1145 #[turbo_tasks::function]
1146 pub async fn project_path(self: Vc<Self>) -> Result<Vc<FileSystemPath>> {
1147 let this = self.await?;
1148 let root = self.project_root_path().await?;
1149 Ok(root.join(&this.project_path)?.cell())
1150 }
1151
1152 #[turbo_tasks::function]
1153 pub(super) fn env(&self) -> Vc<Box<dyn ProcessEnv>> {
1154 *self.env
1155 }
1156
1157 #[turbo_tasks::function]
1158 pub async fn ci_has_next_support(&self) -> Result<Vc<bool>> {
1159 Ok(Vc::cell(
1160 self.env.read(rcstr!("NOW_BUILDER")).await?.is_some(),
1161 ))
1162 }
1163
1164 #[turbo_tasks::function]
1165 pub(super) fn current_node_js_version(&self) -> Vc<NodeJsVersion> {
1166 NodeJsVersion::Static(ResolvedVc::cell(self.current_node_js_version.clone())).cell()
1167 }
1168
1169 #[turbo_tasks::function]
1170 pub fn next_config(&self) -> Vc<NextConfig> {
1171 *self.next_config
1172 }
1173
1174 #[turbo_tasks::function]
1177 pub async fn issue_filter(self: Vc<Self>) -> Result<Vc<IssueFilter>> {
1178 let ignore_rules = self.next_config().turbopack_ignore_issue_rules().await?;
1179 Ok(IssueFilter::warnings_and_foreign_errors()
1180 .with_ignore_rules(ignore_rules.to_vec())
1181 .cell())
1182 }
1183
1184 #[turbo_tasks::function]
1185 pub(super) fn is_persistent_caching_enabled(&self) -> Vc<bool> {
1186 Vc::cell(self.is_persistent_caching_enabled)
1187 }
1188
1189 #[turbo_tasks::function]
1190 pub(super) fn next_mode(&self) -> Vc<NextMode> {
1191 *self.mode
1192 }
1193
1194 #[turbo_tasks::function]
1195 pub(super) fn is_watch_enabled(&self) -> Result<Vc<bool>> {
1196 Ok(Vc::cell(self.watch.enable))
1197 }
1198
1199 #[turbo_tasks::function]
1200 pub(super) fn should_write_routes_hashes_manifest(&self) -> Result<Vc<bool>> {
1201 Ok(Vc::cell(self.write_routes_hashes_manifest))
1202 }
1203
1204 #[turbo_tasks::function]
1205 pub fn deferred_entries(&self) -> Vc<Vec<RcStr>> {
1206 Vc::cell(self.deferred_entries.clone())
1207 }
1208
1209 #[turbo_tasks::function]
1210 pub(super) async fn per_page_module_graph(&self) -> Result<Vc<bool>> {
1211 Ok(Vc::cell(*self.mode.await? == NextMode::Development))
1212 }
1213
1214 #[turbo_tasks::function]
1215 pub(super) fn encryption_key(&self) -> Vc<RcStr> {
1216 Vc::cell(self.encryption_key.clone())
1217 }
1218
1219 #[turbo_tasks::function]
1220 pub(super) fn no_mangling(&self) -> Vc<bool> {
1221 Vc::cell(self.no_mangling)
1222 }
1223
1224 #[turbo_tasks::function]
1225 pub(super) async fn execution_context(self: Vc<Self>) -> Result<Vc<ExecutionContext>> {
1226 let node_root = self.node_root().owned().await?;
1227 let next_mode = self.next_mode().await?;
1228 let strategy = *self
1229 .next_config()
1230 .turbopack_plugin_runtime_strategy()
1231 .await?;
1232 let node_backend = match strategy {
1233 #[cfg(feature = "worker_pool")]
1234 TurbopackPluginRuntimeStrategy::WorkerThreads => worker_threads_backend(),
1235 #[cfg(feature = "process_pool")]
1236 TurbopackPluginRuntimeStrategy::ChildProcesses => child_process_backend(),
1237 };
1238
1239 let node_execution_chunking_context = Vc::upcast(
1240 NodeJsChunkingContext::builder(
1241 self.project_root_path().owned().await?,
1242 node_root.join("build")?,
1243 self.node_root_to_root_path().owned().await?,
1244 node_root.join("build")?,
1245 node_root.join("build/chunks")?,
1246 node_root.join("build/assets")?,
1247 node_build_environment().to_resolved().await?,
1248 next_mode.runtime_type(),
1249 )
1250 .source_maps(*self.next_config().server_source_maps().await?)
1251 .build(),
1252 );
1253
1254 Ok(ExecutionContext::new(
1255 self.project_path().owned().await?,
1256 node_execution_chunking_context,
1257 self.env(),
1258 node_backend,
1259 ))
1260 }
1261
1262 #[turbo_tasks::function]
1263 pub(super) async fn client_compile_time_info(&self) -> Result<Vc<CompileTimeInfo>> {
1264 let next_mode = self.mode.await?;
1265 Ok(get_client_compile_time_info(
1266 self.browserslist_query.clone(),
1267 self.define_env.client(),
1268 self.next_config.report_system_env_inlining(),
1269 next_mode.is_development(),
1270 ))
1271 }
1272
1273 #[turbo_tasks::function]
1274 pub async fn get_all_endpoint_groups(
1275 self: Vc<Self>,
1276 app_dir_only: bool,
1277 ) -> Result<Vc<EndpointGroups>> {
1278 Ok(self.get_all_endpoint_groups_with_app_route_filter(app_dir_only, None))
1279 }
1280
1281 #[turbo_tasks::function]
1282 pub async fn get_all_endpoint_groups_with_app_route_filter(
1283 self: Vc<Self>,
1284 app_dir_only: bool,
1285 app_route_filter: Option<Vec<RcStr>>,
1286 ) -> Result<Vc<EndpointGroups>> {
1287 let mut endpoint_groups = Vec::new();
1288
1289 let entrypoints = self
1290 .entrypoints_with_app_route_filter(app_route_filter)
1291 .await?;
1292 let mut add_pages_entries = false;
1293
1294 if let Some(middleware) = &entrypoints.middleware {
1295 endpoint_groups.push((
1296 EndpointGroupKey::Middleware,
1297 EndpointGroup::from(middleware.endpoint),
1298 ));
1299 }
1300
1301 if let Some(instrumentation) = &entrypoints.instrumentation {
1302 endpoint_groups.push((
1303 EndpointGroupKey::Instrumentation,
1304 EndpointGroup::from(instrumentation.node_js),
1305 ));
1306 endpoint_groups.push((
1307 EndpointGroupKey::InstrumentationEdge,
1308 EndpointGroup::from(instrumentation.edge),
1309 ));
1310 }
1311
1312 for (key, route) in entrypoints.routes.iter() {
1313 match route {
1314 Route::Page {
1315 html_endpoint,
1316 data_endpoint,
1317 } => {
1318 if !app_dir_only {
1319 endpoint_groups.push((
1320 EndpointGroupKey::Route(key.clone()),
1321 EndpointGroup {
1322 primary: vec![EndpointGroupEntry {
1323 endpoint: *html_endpoint,
1324 sub_name: None,
1325 }],
1326 additional: data_endpoint
1328 .iter()
1329 .map(|endpoint| EndpointGroupEntry {
1330 endpoint: *endpoint,
1331 sub_name: None,
1332 })
1333 .collect(),
1334 },
1335 ));
1336 add_pages_entries = true;
1337 }
1338 }
1339 Route::PageApi { endpoint } => {
1340 if !app_dir_only {
1341 endpoint_groups.push((
1342 EndpointGroupKey::Route(key.clone()),
1343 EndpointGroup::from(*endpoint),
1344 ));
1345 add_pages_entries = true;
1346 }
1347 }
1348 Route::AppPage(page_routes) => {
1349 endpoint_groups.push((
1350 EndpointGroupKey::Route(key.clone()),
1351 EndpointGroup {
1352 primary: page_routes
1353 .iter()
1354 .map(|r| EndpointGroupEntry {
1355 endpoint: r.html_endpoint,
1356 sub_name: Some(r.original_name.clone()),
1357 })
1358 .collect(),
1359 additional: Vec::new(),
1360 },
1361 ));
1362 }
1363 Route::AppRoute {
1364 original_name: _,
1365 endpoint,
1366 } => {
1367 endpoint_groups.push((
1368 EndpointGroupKey::Route(key.clone()),
1369 EndpointGroup::from(*endpoint),
1370 ));
1371 }
1372 Route::Conflict => {
1373 tracing::info!("WARN: conflict");
1374 }
1375 }
1376 }
1377
1378 if add_pages_entries {
1379 endpoint_groups.push((
1380 EndpointGroupKey::PagesError,
1381 EndpointGroup::from(entrypoints.pages_error_endpoint),
1382 ));
1383 endpoint_groups.push((
1384 EndpointGroupKey::PagesApp,
1385 EndpointGroup::from(entrypoints.pages_app_endpoint),
1386 ));
1387 endpoint_groups.push((
1388 EndpointGroupKey::PagesDocument,
1389 EndpointGroup::from(entrypoints.pages_document_endpoint),
1390 ));
1391 }
1392
1393 Ok(Vc::cell(endpoint_groups))
1394 }
1395
1396 #[turbo_tasks::function]
1397 pub async fn get_all_endpoints(self: Vc<Self>, app_dir_only: bool) -> Result<Vc<Endpoints>> {
1398 let mut endpoints = Vec::new();
1399 for (_key, group) in self.get_all_endpoint_groups(app_dir_only).await?.iter() {
1400 for entry in group.primary.iter() {
1401 endpoints.push(entry.endpoint);
1402 }
1403 for entry in group.additional.iter() {
1404 endpoints.push(entry.endpoint);
1405 }
1406 }
1407
1408 Ok(Vc::cell(endpoints))
1409 }
1410
1411 #[turbo_tasks::function]
1412 pub async fn get_all_entries(self: Vc<Self>) -> Result<Vc<GraphEntries>> {
1413 let mut modules = self
1414 .get_all_endpoints(false)
1415 .await?
1416 .iter()
1417 .map(async |endpoint| Ok(endpoint.entries().owned().await?))
1418 .try_flat_join()
1419 .await?;
1420 modules.extend(self.client_main_modules().await?.iter().cloned());
1421 Ok(Vc::cell(modules))
1422 }
1423
1424 #[turbo_tasks::function]
1425 pub async fn get_all_additional_entries(
1426 self: Vc<Self>,
1427 graphs: Vc<ModuleGraph>,
1428 ) -> Result<Vc<GraphEntries>> {
1429 let modules = self
1430 .get_all_endpoints(false)
1431 .await?
1432 .iter()
1433 .map(async |endpoint| Ok(endpoint.additional_entries(graphs).owned().await?))
1434 .try_flat_join()
1435 .await?;
1436 Ok(Vc::cell(modules))
1437 }
1438
1439 #[turbo_tasks::function]
1440 pub async fn module_graph(
1441 self: Vc<Self>,
1442 entry: ResolvedVc<Box<dyn Module>>,
1443 ) -> Result<Vc<ModuleGraph>> {
1444 Ok(if *self.per_page_module_graph().await? {
1445 let is_production = self.next_mode().await?.is_production();
1446 ModuleGraph::from_single_graph(SingleModuleGraph::new_with_entry(
1447 ChunkGroupEntry::Entry(vec![entry]),
1448 is_production,
1449 is_production,
1450 ))
1451 .connect()
1452 } else {
1453 *self.whole_app_module_graphs().await?.full
1454 })
1455 }
1456
1457 #[turbo_tasks::function]
1458 pub async fn module_graph_for_modules(
1459 self: Vc<Self>,
1460 evaluatable_assets: Vc<EvaluatableAssets>,
1461 ) -> Result<Vc<ModuleGraph>> {
1462 Ok(if *self.per_page_module_graph().await? {
1463 let is_production = self.next_mode().await?.is_production();
1464 let entries = evaluatable_assets
1465 .await?
1466 .iter()
1467 .copied()
1468 .map(ResolvedVc::upcast)
1469 .collect();
1470 ModuleGraph::from_single_graph(SingleModuleGraph::new_with_entries(
1471 ResolvedVc::cell(vec![ChunkGroupEntry::Entry(entries)]),
1472 is_production,
1473 is_production,
1474 ))
1475 .connect()
1476 } else {
1477 *self.whole_app_module_graphs().await?.full
1478 })
1479 }
1480
1481 #[turbo_tasks::function]
1482 pub async fn whole_app_module_graphs(
1483 self: ResolvedVc<Self>,
1484 ) -> Result<Vc<BaseAndFullModuleGraph>> {
1485 let module_graphs_op = whole_app_module_graph_operation(self);
1486 let module_graphs_vc = if self.next_mode().await?.is_production() {
1487 module_graphs_op.connect()
1488 } else {
1489 let vc = module_graphs_op.resolve_strongly_consistent().await?;
1492 module_graphs_op.drop_issues();
1493 *vc
1494 };
1495
1496 let execution_context = self.execution_context().await?;
1499 let node_backend = execution_context.node_backend.into_trait_ref().await?;
1500 if *self.is_watch_enabled().await? {
1501 node_backend.scale_down()?;
1502 } else {
1503 node_backend.scale_zero()?;
1504 }
1505
1506 Ok(module_graphs_vc)
1507 }
1508
1509 #[turbo_tasks::function]
1510 pub(super) async fn server_compile_time_info(self: Vc<Self>) -> Result<Vc<CompileTimeInfo>> {
1511 let this = self.await?;
1512 Ok(get_server_compile_time_info(
1513 self.project_path(),
1515 this.define_env.nodejs(),
1516 self.current_node_js_version(),
1517 this.next_config.report_system_env_inlining(),
1518 this.server_hmr,
1519 ))
1520 }
1521
1522 #[turbo_tasks::function]
1523 pub(super) async fn edge_compile_time_info(self: Vc<Self>) -> Result<Vc<CompileTimeInfo>> {
1524 let this = self.await?;
1525 Ok(get_edge_compile_time_info(
1526 self.project_path().owned().await?,
1527 this.define_env.edge(),
1528 self.current_node_js_version(),
1529 this.next_config.report_system_env_inlining(),
1530 ))
1531 }
1532
1533 #[turbo_tasks::function]
1534 pub(super) fn edge_env(&self) -> Vc<EnvMap> {
1535 let edge_env = fxindexmap! {
1536 rcstr!("__NEXT_BUILD_ID") => self.build_id.clone(),
1537 rcstr!("NEXT_SERVER_ACTIONS_ENCRYPTION_KEY") => self.encryption_key.clone(),
1538 rcstr!("__NEXT_PREVIEW_MODE_ID") => self.preview_props.preview_mode_id.clone(),
1539 rcstr!("__NEXT_PREVIEW_MODE_ENCRYPTION_KEY") => self.preview_props.preview_mode_encryption_key.clone(),
1540 rcstr!("__NEXT_PREVIEW_MODE_SIGNING_KEY") => self.preview_props.preview_mode_signing_key.clone(),
1541 };
1542 Vc::cell(edge_env)
1543 }
1544
1545 #[turbo_tasks::function]
1546 pub(super) async fn client_chunking_context(
1547 self: Vc<Self>,
1548 ) -> Result<Vc<Box<dyn ChunkingContext>>> {
1549 let css_url_suffix = self.next_config().asset_suffix_path();
1550 Ok(get_client_chunking_context(ClientChunkingContextOptions {
1551 mode: self.next_mode(),
1552 root_path: self.project_root_path().owned().await?,
1553 client_root: self.client_relative_path().owned().await?,
1554 client_root_to_root_path: rcstr!("/ROOT"),
1555 asset_prefix: self.next_config().computed_asset_prefix(),
1556 environment: self.client_compile_time_info().environment(),
1557 module_id_strategy: self.module_ids(),
1558 export_usage: self.export_usage(),
1559 unused_references: self.unused_references(),
1560 minify: self.next_config().turbo_minify(self.next_mode()),
1561 source_maps: self.next_config().client_source_maps(self.next_mode()),
1562 no_mangling: self.no_mangling(),
1563 scope_hoisting: self.next_config().turbo_scope_hoisting(self.next_mode()),
1564 nested_async_chunking: self
1565 .next_config()
1566 .turbo_nested_async_chunking(self.next_mode(), true),
1567 debug_ids: self.next_config().turbopack_debug_ids(),
1568 should_use_absolute_url_references: self.next_config().inline_css(),
1569 css_url_suffix,
1570 }))
1571 }
1572
1573 #[turbo_tasks::function]
1574 pub(super) async fn server_chunking_context(
1575 self: Vc<Self>,
1576 client_assets: bool,
1577 ) -> Result<Vc<NodeJsChunkingContext>> {
1578 let css_url_suffix = self.next_config().asset_suffix_path();
1579 let options = ServerChunkingContextOptions {
1580 mode: self.next_mode(),
1581 root_path: self.project_root_path().owned().await?,
1582 node_root: self.node_root().owned().await?,
1583 node_root_to_root_path: self.node_root_to_root_path().owned().await?,
1584 environment: self.server_compile_time_info().environment(),
1585 module_id_strategy: self.module_ids(),
1586 export_usage: self.export_usage(),
1587 unused_references: self.unused_references(),
1588 minify: self.next_config().turbo_minify(self.next_mode()),
1589 source_maps: self.next_config().server_source_maps(),
1590 no_mangling: self.no_mangling(),
1591 scope_hoisting: self.next_config().turbo_scope_hoisting(self.next_mode()),
1592 nested_async_chunking: self
1593 .next_config()
1594 .turbo_nested_async_chunking(self.next_mode(), false),
1595 debug_ids: self.next_config().turbopack_debug_ids(),
1596 client_root: self.client_relative_path().owned().await?,
1597 asset_prefix: self.next_config().computed_asset_prefix().owned().await?,
1598 css_url_suffix,
1599 };
1600 Ok(if client_assets {
1601 get_server_chunking_context_with_client_assets(options)
1602 } else {
1603 get_server_chunking_context(options)
1604 })
1605 }
1606
1607 #[turbo_tasks::function]
1608 pub(super) async fn edge_chunking_context(
1609 self: Vc<Self>,
1610 client_assets: bool,
1611 ) -> Result<Vc<Box<dyn ChunkingContext>>> {
1612 let css_url_suffix = self.next_config().asset_suffix_path();
1613 let options = EdgeChunkingContextOptions {
1614 mode: self.next_mode(),
1615 root_path: self.project_root_path().owned().await?,
1616 node_root: self.node_root().owned().await?,
1617 output_root_to_root_path: self.node_root_to_root_path(),
1618 environment: self.edge_compile_time_info().environment(),
1619 module_id_strategy: self.module_ids(),
1620 export_usage: self.export_usage(),
1621 unused_references: self.unused_references(),
1622 turbo_minify: self.next_config().turbo_minify(self.next_mode()),
1623 turbo_source_maps: self.next_config().server_source_maps(),
1624 no_mangling: self.no_mangling(),
1625 scope_hoisting: self.next_config().turbo_scope_hoisting(self.next_mode()),
1626 nested_async_chunking: self
1627 .next_config()
1628 .turbo_nested_async_chunking(self.next_mode(), false),
1629 client_root: self.client_relative_path().owned().await?,
1630 asset_prefix: self.next_config().computed_asset_prefix().owned().await?,
1631 css_url_suffix,
1632 };
1633 Ok(if client_assets {
1634 get_edge_chunking_context_with_client_assets(options)
1635 } else {
1636 get_edge_chunking_context(options)
1637 })
1638 }
1639
1640 #[turbo_tasks::function]
1641 pub(super) fn runtime_chunking_context(
1642 self: Vc<Self>,
1643 client_assets: bool,
1644 runtime: NextRuntime,
1645 ) -> Vc<Box<dyn ChunkingContext>> {
1646 match runtime {
1647 NextRuntime::Edge => self.edge_chunking_context(client_assets),
1648 NextRuntime::NodeJs => Vc::upcast(self.server_chunking_context(client_assets)),
1649 }
1650 }
1651
1652 #[turbo_tasks::function]
1655 async fn collect_project_feature_telemetry(self: Vc<Self>) -> Result<Vc<()>> {
1656 let emit_event = |feature_name: &str, enabled: bool| {
1657 NextFeatureTelemetry::new(feature_name.into(), enabled)
1658 .resolved_cell()
1659 .emit();
1660 };
1661
1662 emit_event(env!("VERGEN_CARGO_TARGET_TRIPLE"), true);
1666
1667 let config = self.next_config();
1671
1672 emit_event(
1673 "skipProxyUrlNormalize",
1674 *config.skip_proxy_url_normalize().await?,
1675 );
1676
1677 emit_event(
1678 "skipTrailingSlashRedirect",
1679 *config.skip_trailing_slash_redirect().await?,
1680 );
1681 emit_event(
1682 "persistentCaching",
1683 *self.is_persistent_caching_enabled().await?,
1684 );
1685
1686 emit_event(
1687 "modularizeImports",
1688 !config.modularize_imports().await?.is_empty(),
1689 );
1690 emit_event(
1691 "transpilePackages",
1692 !config.transpile_packages().await?.is_empty(),
1693 );
1694 emit_event("turbotrace", false);
1695
1696 let compiler_options = config.compiler().await?;
1698 let swc_relay_enabled = compiler_options.relay.is_some();
1699 let styled_components_enabled = compiler_options
1700 .styled_components
1701 .as_ref()
1702 .map(|sc| sc.is_enabled())
1703 .unwrap_or_default();
1704 let react_remove_properties_enabled = compiler_options
1705 .react_remove_properties
1706 .as_ref()
1707 .map(|rc| rc.is_enabled())
1708 .unwrap_or_default();
1709 let remove_console_enabled = compiler_options
1710 .remove_console
1711 .as_ref()
1712 .map(|rc| rc.is_enabled())
1713 .unwrap_or_default();
1714 let emotion_enabled = compiler_options
1715 .emotion
1716 .as_ref()
1717 .map(|e| e.is_enabled())
1718 .unwrap_or_default();
1719
1720 emit_event("swcRelay", swc_relay_enabled);
1721 emit_event("swcStyledComponents", styled_components_enabled);
1722 emit_event("swcReactRemoveProperties", react_remove_properties_enabled);
1723 emit_event("swcRemoveConsole", remove_console_enabled);
1724 emit_event("swcEmotion", emotion_enabled);
1725
1726 Ok(Default::default())
1727 }
1728
1729 #[turbo_tasks::function]
1732 pub async fn entrypoints(self: Vc<Self>) -> Result<Vc<Entrypoints>> {
1733 Ok(self.entrypoints_with_app_route_filter(None))
1734 }
1735
1736 #[turbo_tasks::function]
1737 pub async fn entrypoints_with_app_route_filter(
1738 self: Vc<Self>,
1739 app_route_filter: Option<Vec<RcStr>>,
1740 ) -> Result<Vc<Entrypoints>> {
1741 self.collect_project_feature_telemetry().await?;
1742
1743 let this = self.await?;
1744 let mut routes = FxIndexMap::default();
1745 let app_project = self.app_project();
1746 let pages_project = self.pages_project();
1747
1748 let debug_build_paths_route_keys = this
1750 .debug_build_paths
1751 .as_ref()
1752 .map(DebugBuildPathsRouteKeys::from_debug_build_paths)
1753 .transpose()?;
1754
1755 if let Some(app_project) = &*app_project.await? {
1756 let app_routes = app_project.routes_with_filter(app_route_filter);
1757 routes.extend(
1758 app_routes
1759 .await?
1760 .iter()
1761 .filter(|(k, _)| {
1762 debug_build_paths_route_keys
1763 .as_ref()
1764 .is_none_or(|keys| keys.should_include_app_route(k))
1765 })
1766 .map(|(k, v)| (k.clone(), v.clone())),
1767 );
1768 }
1769
1770 for (pathname, page_route) in &pages_project.routes().await? {
1771 if debug_build_paths_route_keys
1772 .as_ref()
1773 .is_some_and(|keys| !keys.should_include_pages_route(pathname))
1774 {
1775 continue;
1776 }
1777
1778 match routes.entry(pathname.clone()) {
1779 Entry::Occupied(mut entry) => {
1780 ConflictIssue {
1781 path: self.project_path().owned().await?,
1782 title: StyledString::Text(
1783 format!("App Router and Pages Router both match path: {pathname}")
1784 .into(),
1785 )
1786 .resolved_cell(),
1787 description: StyledString::Text(
1788 "Next.js does not support having both App Router and Pages Router \
1789 routes matching the same path. Please remove one of the conflicting \
1790 routes."
1791 .into(),
1792 )
1793 .resolved_cell(),
1794 severity: IssueSeverity::Error,
1795 }
1796 .resolved_cell()
1797 .emit();
1798 *entry.get_mut() = Route::Conflict;
1799 }
1800 Entry::Vacant(entry) => {
1801 entry.insert(page_route.clone());
1802 }
1803 }
1804 }
1805
1806 let pages_document_endpoint = self
1807 .pages_project()
1808 .document_endpoint()
1809 .to_resolved()
1810 .await?;
1811 let pages_app_endpoint = self.pages_project().app_endpoint().to_resolved().await?;
1812 let pages_error_endpoint = self.pages_project().error_endpoint().to_resolved().await?;
1813
1814 let middleware = self.find_middleware();
1815 let middleware = if let FindContextFileResult::Found(fs_path, _) = &*middleware.await? {
1816 let is_proxy = fs_path.file_stem() == Some("proxy");
1817 Some(Middleware {
1818 endpoint: self.middleware_endpoint().to_resolved().await?,
1819 is_proxy,
1820 })
1821 } else {
1822 None
1823 };
1824
1825 let instrumentation = self.find_instrumentation();
1826 let instrumentation = if let FindContextFileResult::Found(..) = *instrumentation.await? {
1827 Some(Instrumentation {
1828 node_js: self.instrumentation_endpoint(false).to_resolved().await?,
1829 edge: self.instrumentation_endpoint(true).to_resolved().await?,
1830 })
1831 } else {
1832 None
1833 };
1834
1835 Ok(Entrypoints {
1836 routes,
1837 middleware,
1838 instrumentation,
1839 pages_document_endpoint,
1840 pages_app_endpoint,
1841 pages_error_endpoint,
1842 }
1843 .cell())
1844 }
1845
1846 #[turbo_tasks::function]
1847 async fn edge_middleware_context(self: Vc<Self>) -> Result<Vc<Box<dyn AssetContext>>> {
1848 let mut transitions = vec![];
1849
1850 let app_dir = find_app_dir(self.project_path().owned().await?)
1851 .owned()
1852 .await?;
1853 let app_project = *self.app_project().await?;
1854
1855 let ecmascript_client_reference_transition_name =
1856 app_project.map(|_| AppProject::client_transition_name());
1857
1858 if let Some(app_project) = app_project {
1859 transitions.push((
1860 AppProject::client_transition_name(),
1861 app_project
1862 .edge_ecmascript_client_reference_transition()
1863 .to_resolved()
1864 .await?,
1865 ));
1866 }
1867
1868 Ok(Vc::upcast(ModuleAssetContext::new(
1869 TransitionOptions {
1870 named_transitions: transitions.clone().into_iter().collect(),
1871 ..Default::default()
1872 }
1873 .cell(),
1874 self.edge_compile_time_info(),
1875 get_server_module_options_context(
1876 self.project_path().owned().await?,
1877 self.execution_context(),
1878 ServerContextType::Middleware {
1879 app_dir: app_dir.clone(),
1880 ecmascript_client_reference_transition_name:
1881 ecmascript_client_reference_transition_name.clone(),
1882 },
1883 self.next_mode(),
1884 self.next_config(),
1885 NextRuntime::Edge,
1886 self.encryption_key(),
1887 self.edge_compile_time_info().environment(),
1888 self.client_compile_time_info().environment(),
1889 ),
1890 get_edge_resolve_options_context(
1891 self.project_path().owned().await?,
1892 ServerContextType::Middleware {
1893 app_dir: app_dir.clone(),
1894 ecmascript_client_reference_transition_name:
1895 ecmascript_client_reference_transition_name.clone(),
1896 },
1897 self.next_mode(),
1898 self.next_config(),
1899 self.execution_context(),
1900 None, ),
1902 Layer::new_with_user_friendly_name(
1903 rcstr!("middleware-edge"),
1904 rcstr!("Edge Middleware"),
1905 ),
1906 )))
1907 }
1908
1909 #[turbo_tasks::function]
1910 async fn node_middleware_context(self: Vc<Self>) -> Result<Vc<Box<dyn AssetContext>>> {
1911 let mut transitions = vec![];
1912
1913 let app_dir = find_app_dir(self.project_path().owned().await?)
1914 .owned()
1915 .await?;
1916 let app_project = *self.app_project().await?;
1917
1918 let ecmascript_client_reference_transition_name =
1919 app_project.map(|_| AppProject::client_transition_name());
1920
1921 if let Some(app_project) = app_project {
1922 transitions.push((
1923 AppProject::client_transition_name(),
1924 app_project
1925 .edge_ecmascript_client_reference_transition()
1926 .to_resolved()
1927 .await?,
1928 ));
1929 }
1930
1931 Ok(Vc::upcast(ModuleAssetContext::new(
1932 TransitionOptions {
1933 named_transitions: transitions.clone().into_iter().collect(),
1934 ..Default::default()
1935 }
1936 .cell(),
1937 self.server_compile_time_info(),
1938 get_server_module_options_context(
1939 self.project_path().owned().await?,
1940 self.execution_context(),
1941 ServerContextType::Middleware {
1942 app_dir: app_dir.clone(),
1943 ecmascript_client_reference_transition_name:
1944 ecmascript_client_reference_transition_name.clone(),
1945 },
1946 self.next_mode(),
1947 self.next_config(),
1948 NextRuntime::NodeJs,
1949 self.encryption_key(),
1950 self.server_compile_time_info().environment(),
1951 self.client_compile_time_info().environment(),
1952 ),
1953 get_server_resolve_options_context(
1954 self.project_path().owned().await?,
1955 ServerContextType::Middleware {
1956 app_dir: app_dir.clone(),
1957 ecmascript_client_reference_transition_name,
1958 },
1959 self.next_mode(),
1960 self.next_config(),
1961 self.execution_context(),
1962 None, ),
1964 Layer::new_with_user_friendly_name(rcstr!("middleware"), rcstr!("Middleware")),
1965 )))
1966 }
1967
1968 #[turbo_tasks::function]
1969 async fn find_middleware(self: Vc<Self>) -> Result<Vc<FindContextFileResult>> {
1970 Ok(find_context_file(
1971 self.project_path().owned().await?,
1972 middleware_files(self.next_config().page_extensions()),
1973 false,
1975 ))
1976 }
1977
1978 #[turbo_tasks::function]
1979 async fn middleware_endpoint(self: Vc<Self>) -> Result<Vc<Box<dyn Endpoint>>> {
1980 let middleware = self.find_middleware();
1981 let FindContextFileResult::Found(fs_path, _) = &*middleware.await? else {
1982 return Ok(Vc::upcast(EmptyEndpoint::new(self)));
1983 };
1984 let source = Vc::upcast(FileSource::new(fs_path.clone()));
1985 let app_dir = find_app_dir(self.project_path().owned().await?)
1986 .owned()
1987 .await?;
1988 let ecmascript_client_reference_transition_name = (*self.app_project().await?)
1989 .as_ref()
1990 .map(|_| AppProject::client_transition_name());
1991
1992 let is_proxy = fs_path.file_stem() == Some("proxy");
1993 let config = parse_segment_config_from_source(
1994 source,
1995 if is_proxy {
1996 ParseSegmentMode::Proxy
1997 } else {
1998 ParseSegmentMode::Base
1999 },
2000 );
2001 let runtime = config.await?.runtime.unwrap_or(if is_proxy {
2002 NextRuntime::NodeJs
2003 } else {
2004 NextRuntime::Edge
2005 });
2006
2007 let middleware_asset_context = match runtime {
2008 NextRuntime::NodeJs => self.node_middleware_context(),
2009 NextRuntime::Edge => self.edge_middleware_context(),
2010 };
2011
2012 Ok(Vc::upcast(MiddlewareEndpoint::new(
2013 self,
2014 middleware_asset_context,
2015 source,
2016 app_dir.clone(),
2017 ecmascript_client_reference_transition_name,
2018 config,
2019 runtime,
2020 )))
2021 }
2022
2023 #[turbo_tasks::function]
2024 async fn node_instrumentation_context(self: Vc<Self>) -> Result<Vc<Box<dyn AssetContext>>> {
2025 let mut transitions = vec![];
2026
2027 let app_dir = find_app_dir(self.project_path().owned().await?)
2028 .owned()
2029 .await?;
2030 let app_project = &*self.app_project().await?;
2031
2032 let ecmascript_client_reference_transition_name = app_project
2033 .as_ref()
2034 .map(|_| AppProject::client_transition_name());
2035
2036 if let Some(app_project) = app_project {
2037 transitions.push((
2038 AppProject::client_transition_name(),
2039 app_project
2040 .ecmascript_client_reference_transition()
2041 .to_resolved()
2042 .await?,
2043 ));
2044 }
2045
2046 Ok(Vc::upcast(ModuleAssetContext::new(
2047 TransitionOptions {
2048 named_transitions: transitions.into_iter().collect(),
2049 ..Default::default()
2050 }
2051 .cell(),
2052 self.server_compile_time_info(),
2053 get_server_module_options_context(
2054 self.project_path().owned().await?,
2055 self.execution_context(),
2056 ServerContextType::Instrumentation {
2057 app_dir: app_dir.clone(),
2058 ecmascript_client_reference_transition_name:
2059 ecmascript_client_reference_transition_name.clone(),
2060 },
2061 self.next_mode(),
2062 self.next_config(),
2063 NextRuntime::NodeJs,
2064 self.encryption_key(),
2065 self.server_compile_time_info().environment(),
2066 self.client_compile_time_info().environment(),
2067 ),
2068 get_server_resolve_options_context(
2069 self.project_path().owned().await?,
2070 ServerContextType::Instrumentation {
2071 app_dir: app_dir.clone(),
2072 ecmascript_client_reference_transition_name,
2073 },
2074 self.next_mode(),
2075 self.next_config(),
2076 self.execution_context(),
2077 None, ),
2079 Layer::new_with_user_friendly_name(
2080 rcstr!("instrumentation"),
2081 rcstr!("Instrumentation"),
2082 ),
2083 )))
2084 }
2085
2086 #[turbo_tasks::function]
2087 async fn edge_instrumentation_context(self: Vc<Self>) -> Result<Vc<Box<dyn AssetContext>>> {
2088 let mut transitions = vec![];
2089
2090 let app_dir = find_app_dir(self.project_path().owned().await?)
2091 .owned()
2092 .await?;
2093 let app_project = &*self.app_project().await?;
2094
2095 let ecmascript_client_reference_transition_name = app_project
2096 .as_ref()
2097 .map(|_| AppProject::client_transition_name());
2098
2099 if let Some(app_project) = app_project {
2100 transitions.push((
2101 AppProject::client_transition_name(),
2102 app_project
2103 .edge_ecmascript_client_reference_transition()
2104 .to_resolved()
2105 .await?,
2106 ));
2107 }
2108
2109 Ok(Vc::upcast(ModuleAssetContext::new(
2110 TransitionOptions {
2111 named_transitions: transitions.into_iter().collect(),
2112 ..Default::default()
2113 }
2114 .cell(),
2115 self.edge_compile_time_info(),
2116 get_server_module_options_context(
2117 self.project_path().owned().await?,
2118 self.execution_context(),
2119 ServerContextType::Instrumentation {
2120 app_dir: app_dir.clone(),
2121 ecmascript_client_reference_transition_name:
2122 ecmascript_client_reference_transition_name.clone(),
2123 },
2124 self.next_mode(),
2125 self.next_config(),
2126 NextRuntime::Edge,
2127 self.encryption_key(),
2128 self.edge_compile_time_info().environment(),
2129 self.client_compile_time_info().environment(),
2130 ),
2131 get_edge_resolve_options_context(
2132 self.project_path().owned().await?,
2133 ServerContextType::Instrumentation {
2134 app_dir: app_dir.clone(),
2135 ecmascript_client_reference_transition_name,
2136 },
2137 self.next_mode(),
2138 self.next_config(),
2139 self.execution_context(),
2140 None, ),
2142 Layer::new_with_user_friendly_name(
2143 rcstr!("instrumentation-edge"),
2144 rcstr!("Edge Instrumentation"),
2145 ),
2146 )))
2147 }
2148
2149 #[turbo_tasks::function]
2150 async fn find_instrumentation(self: Vc<Self>) -> Result<Vc<FindContextFileResult>> {
2151 Ok(find_context_file(
2152 self.project_path().owned().await?,
2153 instrumentation_files(self.next_config().page_extensions()),
2154 false,
2156 ))
2157 }
2158
2159 #[turbo_tasks::function]
2160 async fn instrumentation_endpoint(
2161 self: Vc<Self>,
2162 is_edge: bool,
2163 ) -> Result<Vc<Box<dyn Endpoint>>> {
2164 let instrumentation = self.find_instrumentation();
2165 let FindContextFileResult::Found(fs_path, _) = &*instrumentation.await? else {
2166 return Ok(Vc::upcast(EmptyEndpoint::new(self)));
2167 };
2168 let source = Vc::upcast(FileSource::new(fs_path.clone()));
2169 let app_dir = find_app_dir(self.project_path().owned().await?)
2170 .owned()
2171 .await?;
2172 let ecmascript_client_reference_transition_name = (*self.app_project().await?)
2173 .as_ref()
2174 .map(|_| AppProject::client_transition_name());
2175
2176 let instrumentation_asset_context = if is_edge {
2177 self.edge_instrumentation_context()
2178 } else {
2179 self.node_instrumentation_context()
2180 };
2181
2182 Ok(Vc::upcast(InstrumentationEndpoint::new(
2183 self,
2184 instrumentation_asset_context,
2185 source,
2186 is_edge,
2187 app_dir.clone(),
2188 ecmascript_client_reference_transition_name,
2189 )))
2190 }
2191
2192 #[turbo_tasks::function]
2193 pub async fn emit_all_output_assets(
2194 self: Vc<Self>,
2195 output_assets: OperationVc<OutputAssets>,
2196 ) -> Result<()> {
2197 let span = tracing::info_span!("emitting");
2198 async move {
2199 let all_output_assets = all_assets_from_entries_operation(output_assets);
2200
2201 let client_relative_path = self.client_relative_path().owned().await?;
2202 let node_root = self.node_root().owned().await?;
2203
2204 if let Some(map) = self.await?.versioned_content_map {
2205 map.insert_output_assets(
2206 all_output_assets,
2207 node_root.clone(),
2208 client_relative_path.clone(),
2209 node_root.clone(),
2210 )
2211 .as_side_effect()
2212 .await?;
2213
2214 Ok(())
2215 } else {
2216 emit_assets(
2217 all_output_assets.connect(),
2218 node_root.clone(),
2219 client_relative_path.clone(),
2220 node_root.clone(),
2221 )
2222 .as_side_effect()
2223 .await?;
2224
2225 Ok(())
2226 }
2227 }
2228 .instrument(span)
2229 .await
2230 }
2231
2232 #[turbo_tasks::function]
2235 async fn hmr_root_path(self: Vc<Self>, target: HmrTarget) -> Result<Vc<FileSystemPath>> {
2236 Ok(match target {
2237 HmrTarget::Client => self.client_relative_path(),
2238 HmrTarget::Server => self.node_root(),
2239 })
2240 }
2241
2242 #[turbo_tasks::function]
2244 async fn hmr_content(
2245 self: Vc<Self>,
2246 chunk_name: RcStr,
2247 target: HmrTarget,
2248 ) -> Result<Vc<OptionVersionedContent>> {
2249 if let Some(map) = self.await?.versioned_content_map {
2250 let content = map.get(self.hmr_root_path(target).await?.join(&chunk_name)?);
2251 Ok(content)
2252 } else {
2253 bail!("must be in dev mode to hmr")
2254 }
2255 }
2256
2257 #[turbo_tasks::function]
2260 pub async fn hmr_version_state(
2261 self: ResolvedVc<Self>,
2262 chunk_name: RcStr,
2263 target: HmrTarget,
2264 session: TransientInstance<()>,
2265 ) -> Result<Vc<VersionState>> {
2266 let _ = session;
2269
2270 #[turbo_tasks::function(operation)]
2271 async fn hmr_version_operation(
2272 this: ResolvedVc<Project>,
2273 chunk_name: RcStr,
2274 target: HmrTarget,
2275 ) -> Result<Vc<Box<dyn Version>>> {
2276 let content = this.hmr_content(chunk_name, target).await?;
2277 if let Some(content) = &*content {
2278 Ok(content.version())
2279 } else {
2280 Ok(Vc::upcast(NotFoundVersion::new()))
2281 }
2282 }
2283 let version_op = hmr_version_operation(self, chunk_name, target);
2284
2285 let state = VersionState::new(
2289 version_op
2290 .read_trait_strongly_consistent()
2291 .untracked()
2292 .await?,
2293 )
2294 .await?;
2295 Ok(state)
2296 }
2297
2298 #[turbo_tasks::function]
2301 pub async fn hmr_update(
2302 self: Vc<Self>,
2303 chunk_name: RcStr,
2304 target: HmrTarget,
2305 from: Vc<VersionState>,
2306 ) -> Result<Vc<Update>> {
2307 let from = from.get();
2308 let content = self.hmr_content(chunk_name, target).await?;
2309 if let Some(content) = *content {
2310 Ok(content.update(from))
2311 } else {
2312 Ok(Update::Missing.cell())
2313 }
2314 }
2315
2316 #[turbo_tasks::function]
2321 pub async fn hmr_chunk_names(self: Vc<Self>, target: HmrTarget) -> Result<Vc<Vec<RcStr>>> {
2322 if let Some(map) = self.await?.versioned_content_map {
2323 Ok(map.keys_in_path(self.hmr_root_path(target).owned().await?))
2324 } else {
2325 bail!("must be in dev mode to hmr")
2326 }
2327 }
2328
2329 #[turbo_tasks::function]
2332 pub async fn server_changed(self: Vc<Self>, roots: Vc<OutputAssets>) -> Result<Vc<Completion>> {
2333 let path = self.node_root().owned().await?;
2334 Ok(any_output_changed(roots, path, true))
2335 }
2336
2337 #[turbo_tasks::function]
2340 pub async fn client_changed(self: Vc<Self>, roots: Vc<OutputAssets>) -> Result<Vc<Completion>> {
2341 let path = self.client_root().owned().await?;
2342 Ok(any_output_changed(roots, path, false))
2343 }
2344
2345 #[turbo_tasks::function]
2346 pub async fn client_main_modules(self: Vc<Self>) -> Result<Vc<GraphEntries>> {
2347 let pages_project = self.pages_project();
2348 let mut modules = vec![ChunkGroupEntry::Entry(vec![
2349 pages_project.client_main_module().to_resolved().await?,
2350 ])];
2351
2352 if let Some(app_project) = *self.app_project().await? {
2353 modules.push(ChunkGroupEntry::Entry(vec![
2354 app_project.client_main_module().to_resolved().await?,
2355 ]));
2356 }
2357
2358 Ok(Vc::cell(modules))
2359 }
2360
2361 #[turbo_tasks::function]
2363 pub async fn module_ids(self: Vc<Self>) -> Result<Vc<ModuleIdStrategy>> {
2364 let module_id_strategy = *self.next_config().module_ids(self.next_mode()).await?;
2365 match module_id_strategy {
2366 ModuleIdStrategyConfig::Named => Ok(ModuleIdStrategy {
2367 module_id_map: None,
2368 fallback: ModuleIdFallback::Ident,
2369 }
2370 .cell()),
2371 ModuleIdStrategyConfig::Deterministic => {
2372 let module_graphs = self.whole_app_module_graphs().await?;
2373 Ok(get_global_module_id_strategy(*module_graphs.full))
2374 }
2375 }
2376 }
2377
2378 #[turbo_tasks::function]
2380 async fn binding_usage_info(self: Vc<Self>) -> Result<Vc<BindingUsageInfo>> {
2381 let module_graphs = self.whole_app_module_graphs().await?;
2382 Ok(module_graphs
2383 .binding_usage_info
2384 .context("No binding usage info")?
2385 .connect())
2386 }
2387
2388 #[turbo_tasks::function]
2390 pub async fn export_usage(self: Vc<Self>) -> Result<Vc<OptionBindingUsageInfo>> {
2391 if *self
2392 .next_config()
2393 .turbopack_remove_unused_exports(self.next_mode())
2394 .await?
2395 {
2396 Ok(Vc::cell(Some(
2397 self.binding_usage_info().to_resolved().await?,
2398 )))
2399 } else {
2400 Ok(Vc::cell(None))
2401 }
2402 }
2403
2404 #[turbo_tasks::function]
2406 pub async fn unused_references(self: Vc<Self>) -> Result<Vc<UnusedReferences>> {
2407 if *self
2408 .next_config()
2409 .turbopack_remove_unused_imports(self.next_mode())
2410 .await?
2411 {
2412 Ok(self.binding_usage_info().unused_references())
2413 } else {
2414 Ok(Vc::cell(Default::default()))
2415 }
2416 }
2417
2418 #[turbo_tasks::function]
2419 pub async fn with_next_config(&self, next_config: Vc<NextConfig>) -> Result<Vc<Self>> {
2420 Ok(Self {
2421 next_config: next_config.to_resolved().await?,
2422 ..(*self).clone()
2423 }
2424 .cell())
2425 }
2426}
2427
2428#[turbo_tasks::function(operation, root)]
2431async fn whole_app_module_graph_operation(
2432 project: ResolvedVc<Project>,
2433) -> Result<Vc<BaseAndFullModuleGraph>> {
2434 let span = tracing::info_span!("whole app module graph", modules = Empty);
2435 let span_clone = span.clone();
2436 async move {
2437 let next_mode = project.next_mode();
2438 let next_mode_ref = next_mode.await?;
2439 let should_trace = next_mode_ref.is_production();
2440 let should_read_binding_usage = next_mode_ref.is_production();
2441 let base_single_module_graph = SingleModuleGraph::new_with_entries(
2442 project.get_all_entries().to_resolved().await?,
2443 should_trace,
2444 should_read_binding_usage,
2445 );
2446 let base_visited_modules = VisitedModules::from_graph(base_single_module_graph);
2447
2448 let base = ModuleGraph::from_single_graph(base_single_module_graph);
2449
2450 let turbopack_remove_unused_imports = *project
2451 .next_config()
2452 .turbopack_remove_unused_imports(next_mode)
2453 .await?;
2454
2455 let base = if turbopack_remove_unused_imports {
2456 let binding_usage_info = compute_binding_usage_info(base, true);
2459 ModuleGraph::from_single_graph_without_unused_references(
2460 base_single_module_graph,
2461 binding_usage_info,
2462 )
2463 } else {
2464 base
2465 };
2466
2467 let additional_entries = project
2468 .get_all_additional_entries(base.connect())
2469 .to_resolved()
2470 .await?;
2471
2472 let additional_module_graph = SingleModuleGraph::new_with_entries_visited(
2473 additional_entries,
2474 base_visited_modules,
2475 should_trace,
2476 should_read_binding_usage,
2477 );
2478
2479 if !span.is_disabled() {
2480 let base_module_count = base_single_module_graph
2481 .connect()
2482 .module_count()
2483 .untracked()
2484 .owned()
2485 .await?;
2486 let additional_module_count = additional_module_graph
2487 .connect()
2488 .module_count()
2489 .untracked()
2490 .owned()
2491 .await?;
2492 span.record("modules", base_module_count + additional_module_count);
2493 }
2494
2495 let graphs = vec![base_single_module_graph, additional_module_graph];
2496
2497 let (full, binding_usage_info) = if turbopack_remove_unused_imports {
2498 let full_with_unused_references = ModuleGraph::from_graphs(graphs.clone());
2499 let binding_usage_info = compute_binding_usage_info(full_with_unused_references, true);
2500 (
2501 ModuleGraph::from_graphs_without_unused_references(graphs, binding_usage_info),
2502 Some(binding_usage_info),
2503 )
2504 } else {
2505 (ModuleGraph::from_graphs(graphs), None)
2506 };
2507
2508 Ok(BaseAndFullModuleGraph {
2509 base: base.connect().to_resolved().await?,
2510 full: full.connect().to_resolved().await?,
2511 binding_usage_info,
2512 }
2513 .cell())
2514 }
2515 .instrument(span_clone)
2516 .await
2517}
2518
2519#[turbo_tasks::value(shared)]
2520pub struct BaseAndFullModuleGraph {
2521 pub base: ResolvedVc<ModuleGraph>,
2523 pub full: ResolvedVc<ModuleGraph>,
2525 pub binding_usage_info: Option<OperationVc<BindingUsageInfo>>,
2527}
2528
2529#[turbo_tasks::function]
2530async fn any_output_changed(
2531 roots: Vc<OutputAssets>,
2532 path: FileSystemPath,
2533 server: bool,
2534) -> Result<Vc<Completion>> {
2535 let all_assets = expand_output_assets(
2536 roots
2537 .await?
2538 .into_iter()
2539 .map(|&a| ExpandOutputAssetsInput::Asset(a)),
2540 true,
2541 )
2542 .await?;
2543 let completions = all_assets
2544 .into_iter()
2545 .map(|m| {
2546 let path = path.clone();
2547
2548 async move {
2549 let asset_path = m.path().await?;
2550 if !asset_path.path.ends_with(".map")
2551 && (!server || !asset_path.path.ends_with(".css"))
2552 && asset_path.is_inside_ref(&path)
2553 {
2554 anyhow::Ok(Some(
2555 content_changed(*ResolvedVc::upcast(m))
2556 .to_resolved()
2557 .await?,
2558 ))
2559 } else {
2560 Ok(None)
2561 }
2562 }
2563 })
2564 .try_flat_join()
2565 .await?;
2566
2567 Ok(Vc::<Completions>::cell(completions).completed())
2568}
2569
2570#[turbo_tasks::function(operation)]
2571fn all_assets_from_entries_operation(
2572 operation: OperationVc<OutputAssets>,
2573) -> Result<Vc<ExpandedOutputAssets>> {
2574 let assets = operation.connect();
2575 Ok(all_assets_from_entries(assets))
2576}