1use std::{
2 iter,
3 path::{Path, PathBuf},
4 sync::Arc,
5 time::{Duration, Instant, SystemTime},
6};
7
8use anyhow::{Context, Result, bail};
9use async_trait::async_trait;
10use bincode::{Decode, Encode};
11use indexmap::map::Entry;
12use next_core::{
13 app_structure::find_app_dir,
14 emit_assets, get_edge_chunking_context, get_edge_chunking_context_with_client_assets,
15 get_edge_compile_time_info, get_edge_resolve_options_context,
16 instrumentation::instrumentation_files,
17 middleware::middleware_files,
18 mode::NextMode,
19 next_app::{AppPage, AppPath},
20 next_client::{
21 ClientChunkingContextOptions, ClientContextType, ServiceWorkerChunkingContextOptions,
22 get_client_chunking_context, get_client_compile_time_info,
23 get_client_module_options_context, get_client_resolve_options_context,
24 get_service_worker_chunking_context,
25 },
26 next_config::{
27 DIST_PROFILES_DIR_NAME, ModuleIds as ModuleIdStrategyConfig, NextConfig, OutputType,
28 TurbopackPluginRuntimeStrategy,
29 },
30 next_edge::context::EdgeChunkingContextOptions,
31 next_server::{
32 ServerChunkingContextOptions, ServerContextType, get_server_chunking_context,
33 get_server_chunking_context_with_client_assets, get_server_compile_time_info,
34 get_server_module_options_context, get_server_resolve_options_context,
35 get_tracing_compile_time_info,
36 },
37 next_telemetry::ProjectFeatureUsageSummary,
38 parse_segment_config_from_source,
39 segment_config::ParseSegmentMode,
40 util::{NextRuntime, OptionEnvMap},
41};
42use rustc_hash::{FxHashMap, FxHashSet};
43use serde::{Deserialize, Serialize};
44use tracing::{Instrument, field::Empty};
45use turbo_rcstr::{RcStr, rcstr};
46use turbo_tasks::{
47 Completion, Completions, FxIndexMap, InvalidationReason, NonLocalValue, OperationValue,
48 OperationVc, ReadRef, ResolvedVc, State, TransientInstance, TryFlatJoinIterExt, TryJoinIterExt,
49 Vc, debug::ValueDebugFormat, fxindexmap, message_queue::TraceEvent, turbo_tasks,
50};
51use turbo_tasks_env::{EnvMap, ProcessEnv};
52use turbo_tasks_fs::{
53 DiskFileSystem, DiskFileSystemMap, DiskWatcherConfig, FileContent, FileSystem, FileSystemPath,
54 VirtualFileSystem, invalidation,
55};
56use turbo_unix_path::join_path;
57use turbopack::{
58 ModuleAssetContext, evaluate_context::node_build_environment, externals_tracing_module_context,
59 global_module_ids::get_global_module_id_strategy, transition::TransitionOptions,
60};
61use turbopack_core::{
62 PROJECT_FILESYSTEM_NAME,
63 changed::content_changed,
64 chunk::{
65 ChunkingContext, EvaluatableAssets, UnusedReferences,
66 chunk_id_strategy::{ModuleIdFallback, ModuleIdStrategy},
67 },
68 compile_time_info::CompileTimeInfo,
69 context::AssetContext,
70 environment::NodeJsVersion,
71 file_source::FileSource,
72 ident::Layer,
73 issue::{
74 CollectibleIssuesExt, Issue, IssueExt, IssueFilter, IssueSeverity, IssueStage, PlainIssue,
75 StyledString,
76 },
77 module::{Module, Modules},
78 module_graph::{
79 GraphEntries, ModuleGraph, SingleModuleGraph, VisitedModules,
80 binding_usage_info::{
81 BindingUsageInfo, OptionBindingUsageInfo, compute_binding_usage_info,
82 },
83 chunk_group_info::{ChunkGroupEntry, EntryHeuristics},
84 },
85 output::{
86 ExpandOutputAssetsInput, ExpandedOutputAssets, OutputAsset, OutputAssets,
87 expand_output_assets,
88 },
89 reference::all_assets_from_entries,
90 reference_type::{CommonJsReferenceSubType, ReferenceType},
91 resolve::{FindContextFileResult, find_context_file},
92 version::{
93 NotFoundVersion, OptionVersionedContent, Update, Version, VersionState, VersionedContent,
94 },
95};
96use turbopack_ecmascript::async_chunk::proxy::{
97 activation_key_from_chunk_path, lazy_compilation_state,
98};
99#[cfg(all(feature = "process_pool", not(target_family = "wasm")))]
100use turbopack_node::child_process_backend;
101use turbopack_node::execution_context::ExecutionContext;
102#[cfg(feature = "worker_pool")]
103use turbopack_node::worker_threads_backend;
104use turbopack_nodejs::{NodeJsChunkingContext, fs::NodeModulesPathMatcher};
105
106pub use crate::additional_roots::AdditionalRootConfig;
107use crate::{
108 additional_roots::{AdditionalDiskFileSystem, create_additional_root_file_systems},
109 aggregate_hmr::ServerHmrChunkLists,
110 app::{AppProject, OptionAppProject},
111 empty::EmptyEndpoint,
112 entrypoints::Entrypoints,
113 instrumentation::InstrumentationEndpoint,
114 middleware::MiddlewareEndpoint,
115 next_server_nft::{pages_renderer_modules, require_hook_modules},
116 pages::PagesProject,
117 path_utils::convention_file_base_name,
118 route::{
119 Endpoint, EndpointGroup, EndpointGroupEntry, EndpointGroupKey, EndpointGroups, Endpoints,
120 Route,
121 },
122 versioned_content_map::VersionedContentMap,
123};
124
125#[turbo_tasks::task_input]
126#[derive(
127 Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash, OperationValue, Encode, Decode,
128)]
129#[serde(rename_all = "camelCase")]
130pub struct DraftModeOptions {
131 pub preview_mode_id: RcStr,
132 pub preview_mode_encryption_key: RcStr,
133 pub preview_mode_signing_key: RcStr,
134}
135
136#[turbo_tasks::value(transparent)]
138struct DraftModeOptionsValue(DraftModeOptions);
139
140#[turbo_tasks::task_input]
141#[derive(
142 Debug,
143 Default,
144 Serialize,
145 Deserialize,
146 Copy,
147 Clone,
148 PartialEq,
149 Eq,
150 Hash,
151 OperationValue,
152 Encode,
153 Decode,
154)]
155#[serde(rename_all = "camelCase")]
156pub struct WatchOptions {
157 pub enable: bool,
159
160 pub poll_interval: Option<Duration>,
163}
164
165#[turbo_tasks::task_input]
166#[derive(
167 Debug,
168 Default,
169 Serialize,
170 Deserialize,
171 Clone,
172 PartialEq,
173 Eq,
174 Hash,
175 OperationValue,
176 Encode,
177 Decode,
178)]
179#[serde(rename_all = "camelCase")]
180pub struct DebugBuildPaths {
181 pub app: Vec<RcStr>,
182 pub pages: Vec<RcStr>,
183}
184
185struct DebugBuildPathsRouteKeys {
187 app: FxHashSet<RcStr>,
188 pages: FxHashSet<RcStr>,
189}
190
191impl DebugBuildPathsRouteKeys {
192 fn app_route_key_from_debug_path(path: &str) -> Result<RcStr> {
193 let mut segments = path
194 .trim_start_matches('/')
195 .split('/')
196 .filter(|segment| !segment.is_empty())
197 .collect::<Vec<_>>();
198
199 if let Some(last_segment) = segments.last()
200 && (*last_segment == "page"
201 || last_segment.starts_with("page.")
202 || *last_segment == "route"
203 || last_segment.starts_with("route."))
204 {
205 segments.pop();
206 }
207
208 let normalized_path = segments.join("/");
209 Ok(AppPath::from(AppPage::parse(&normalized_path)?)
210 .to_string()
211 .into())
212 }
213
214 fn pages_route_key_from_debug_path(path: &RcStr) -> Result<RcStr> {
215 let file_name = path.rsplit('/').next().unwrap_or(path);
219 let result = if let Some(dot_idx) = file_name.rfind('.') {
220 let ext = &file_name[dot_idx + 1..];
221 if !ext.is_empty() && ext.chars().all(|c| c.is_ascii_alphanumeric()) {
222 let trimmed_len = path.len() - (file_name.len() - dot_idx);
223 path[..trimmed_len].into()
224 } else {
225 path.clone()
226 }
227 } else {
228 path.clone()
229 };
230
231 Ok(if let Some(stripped) = result.strip_suffix("/index") {
233 if stripped.is_empty() {
234 "/".into()
235 } else {
236 stripped.into()
237 }
238 } else {
239 result
240 })
241 }
242
243 fn from_debug_build_paths(paths: &DebugBuildPaths) -> Result<Self> {
244 Ok(Self {
245 app: paths
246 .app
247 .iter()
248 .map(|path| Self::app_route_key_from_debug_path(path))
249 .collect::<Result<_>>()?,
250 pages: paths
251 .pages
252 .iter()
253 .map(Self::pages_route_key_from_debug_path)
254 .collect::<Result<_>>()?,
255 })
256 }
257
258 fn should_include_app_route(&self, route_key: &RcStr) -> bool {
259 if matches!(route_key.as_str(), "/_not-found" | "/_global-error") {
261 return true;
262 }
263 self.app.contains(route_key)
264 }
265
266 fn should_include_pages_route(&self, route_key: &RcStr) -> bool {
267 if matches!(
269 route_key.as_str(),
270 "/_error" | "/_document" | "/_app" | "/404" | "/500"
271 ) {
272 return self.pages.iter().any(|page| {
273 let page = page.as_str();
274 page != "/api" && !page.starts_with("/api/")
275 });
276 }
277 self.pages.contains(route_key)
278 }
279}
280
281#[derive(
282 Debug,
283 Serialize,
284 Deserialize,
285 Clone,
286 PartialEq,
287 Eq,
288 NonLocalValue,
289 OperationValue,
290 Encode,
291 Decode,
292)]
293#[serde(rename_all = "camelCase")]
294pub struct ProjectOptions {
295 pub root_path: RcStr,
307
308 pub project_path: RcStr,
311
312 pub next_config: RcStr,
314
315 pub additional_roots: Vec<AdditionalRootConfig>,
317
318 pub env: Vec<(RcStr, RcStr)>,
320
321 pub define_env: DefineEnv,
323
324 pub watch: WatchOptions,
326
327 pub dev: bool,
329
330 pub encryption_key: RcStr,
332
333 pub build_id: RcStr,
335
336 pub preview_props: DraftModeOptions,
338
339 pub browserslist_query: RcStr,
341
342 pub no_mangling: bool,
345
346 pub write_routes_hashes_manifest: bool,
348
349 pub current_node_js_version: RcStr,
351
352 pub debug_build_paths: Option<DebugBuildPaths>,
355
356 pub deferred_entries: Option<Vec<RcStr>>,
358
359 pub is_persistent_caching_enabled: bool,
361
362 pub next_version: RcStr,
364
365 pub server_hmr: bool,
367}
368
369#[derive(Default)]
374pub struct PartialProjectOptions {
375 pub next_config: Option<RcStr>,
376
377 pub env: Option<Vec<(RcStr, RcStr)>>,
378
379 pub define_env: Option<DefineEnv>,
380
381 pub dev: Option<bool>,
382
383 pub encryption_key: Option<RcStr>,
384
385 pub build_id: Option<RcStr>,
386
387 pub preview_props: Option<DraftModeOptions>,
388
389 pub browserslist_query: Option<RcStr>,
390
391 pub no_mangling: Option<bool>,
392
393 pub write_routes_hashes_manifest: Option<bool>,
394
395 pub debug_build_paths: Option<DebugBuildPaths>,
396}
397
398#[turbo_tasks::task_input]
399#[derive(
400 Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash, OperationValue, Encode, Decode,
401)]
402#[serde(rename_all = "camelCase")]
403pub struct DefineEnv {
404 pub client: Vec<(RcStr, Option<RcStr>)>,
405 pub edge: Vec<(RcStr, Option<RcStr>)>,
406 pub nodejs: Vec<(RcStr, Option<RcStr>)>,
407}
408
409#[derive(PartialEq, Eq, ValueDebugFormat, NonLocalValue, Encode, Decode)]
410pub struct Middleware {
411 pub endpoint: ResolvedVc<Box<dyn Endpoint>>,
412 pub is_proxy: bool,
413}
414
415#[derive(PartialEq, Eq, ValueDebugFormat, NonLocalValue, Encode, Decode)]
416pub struct Instrumentation {
417 pub node_js: ResolvedVc<Box<dyn Endpoint>>,
418 pub edge: ResolvedVc<Box<dyn Endpoint>>,
419}
420
421#[derive(Clone, Debug, PartialEq, Eq, NonLocalValue, OperationValue, Encode, Decode)]
422struct ProjectFileSystemState {
423 project_file_system: OperationVc<DiskFileSystem>,
424 output_file_system: OperationVc<DiskFileSystem>,
425}
426
427#[turbo_tasks::value(evict = "never", eq = "manual", cell = "new")]
428pub struct ProjectContainer {
429 name: RcStr,
430 options_state: State<Option<ProjectOptions>>,
431 file_systems_state: State<Option<ProjectFileSystemState>>,
432 additional_roots_state: State<Vec<(RcStr, AdditionalDiskFileSystem)>>,
433 #[turbo_tasks(debug_ignore, unsafe_ignore)]
434 #[bincode(skip)]
435 fs_map_init_lock: tokio::sync::Mutex<()>,
436 versioned_content_map: Option<ResolvedVc<VersionedContentMap>>,
437}
438
439#[turbo_tasks::value_impl]
440impl ProjectContainer {
441 #[turbo_tasks::function(operation, root)]
442 pub fn new_operation(name: RcStr, dev: bool) -> Result<Vc<Self>> {
443 Ok(ProjectContainer {
444 name,
445 versioned_content_map: if dev {
448 Some(VersionedContentMap::new())
449 } else {
450 None
451 },
452 options_state: State::new(None),
453 file_systems_state: State::new(None),
454 additional_roots_state: State::new(Vec::new()),
455 fs_map_init_lock: tokio::sync::Mutex::new(()),
456 }
457 .cell())
458 }
459}
460
461async fn prepare_project_container_state(
464 container_vc: ResolvedVc<ProjectContainer>,
465 options: ProjectOptions,
466) -> Result<Vec<ReadRef<PlainIssue>>> {
467 let container = container_vc.await?;
468 let fs_map_init_guard = container.fs_map_init_lock.lock().await;
471
472 let map = disk_file_system_map_operation(container_vc);
473 let config_json: serde_json::Value = serde_json::from_str(&options.next_config)?;
474
475 let dist_dir_root = config_json
476 .get("distDirRoot")
477 .and_then(|value| value.as_str())
478 .unwrap_or(".next");
479
480 let watcher_config = DiskWatcherConfig {
481 poll_interval: options.watch.poll_interval,
482 report_invalidation_reason: true,
483 ..Default::default()
484 };
485
486 let denied_paths = vec![
487 RcStr::from(
488 join_path(&options.project_path, dist_dir_root)
489 .context("distDirRoot must stay inside the project root")?,
490 ),
491 RcStr::from(join_path(&options.project_path, DIST_PROFILES_DIR_NAME).unwrap()),
494 ];
495
496 let enable_watch = options.watch.enable;
497 let configured_additional_roots = options.additional_roots.clone();
498 let project_root = options.root_path.clone();
499 let project_path = options.project_path.clone();
500
501 container.options_state.set(Some(options));
505
506 let root_path_op = project_root_path_operation(container_vc);
510
511 let project_fs_op = disk_file_system_operation(
512 PROJECT_FILESYSTEM_NAME,
513 root_path_op,
514 denied_paths,
515 watcher_config,
516 map,
517 );
518
519 let output_fs_op = disk_file_system_operation(
520 rcstr!("output"),
521 root_path_op,
522 Vec::new(),
523 DiskWatcherConfig::default(),
524 DiskFileSystemMap::empty(),
525 );
526
527 container
529 .file_systems_state
530 .set(Some(ProjectFileSystemState {
531 project_file_system: project_fs_op,
532 output_file_system: output_fs_op,
533 }));
534 let project_fs_vc = project_fs_op.resolve().strongly_consistent().await?;
535 let project_fs = project_fs_op.read_strongly_consistent().await?;
536
537 let config_file_name = config_json
538 .get("configFileName")
539 .and_then(|value| value.as_str())
540 .unwrap_or("next.config.js");
541 let config_path = FileSystemPath::new_normalized_unchecked(
542 ResolvedVc::upcast(project_fs_vc),
543 RcStr::default(),
544 )
545 .join(&project_path)?
546 .join(config_file_name)?;
547 let additional_roots = create_additional_root_file_systems(
548 container_vc,
549 configured_additional_roots,
550 &project_root,
551 watcher_config,
552 map,
553 config_path,
554 )
555 .await?;
556
557 let additional_file_systems = additional_roots
558 .roots_by_name
559 .values()
560 .map(|root| root.file_system)
561 .collect::<Vec<_>>();
562
563 container
566 .additional_roots_state
567 .set(additional_roots.roots_by_name.into_iter().collect());
568 drop(fs_map_init_guard);
569
570 fn invalidation_reason(path: &Path) -> impl InvalidationReason + Clone + use<> {
572 invalidation::Initialize {
573 path: RcStr::from(path.to_string_lossy()),
574 }
575 }
576 if enable_watch {
577 project_fs.start_watching().await?;
578 for op in &additional_file_systems {
579 let fs = op.read_strongly_consistent().await?;
580 fs.start_watching().await?;
581 }
582 } else {
583 project_fs.invalidate_with_reason(invalidation_reason);
584 for op in &additional_file_systems {
585 op.read_strongly_consistent()
586 .await?
587 .invalidate_with_reason(invalidation_reason);
588 }
589 }
590
591 output_fs_op
594 .read_strongly_consistent()
595 .await?
596 .invalidate_with_reason(invalidation_reason);
597
598 Ok(additional_roots.issues)
599}
600
601#[turbo_tasks::function(operation, root)]
604pub async fn activate_lazy_chunk_operation(chunk_path: RcStr) -> Result<Vc<bool>> {
605 let Some(key) = activation_key_from_chunk_path(&chunk_path) else {
606 return Ok(Vc::cell(false));
607 };
608 lazy_compilation_state(key).await?.activate();
609 Ok(Vc::cell(true))
610}
611
612#[turbo_tasks::function(operation, root)]
613pub(crate) fn disk_file_system_operation(
614 name: RcStr,
615 canonical_root: OperationVc<RcStr>,
616 denied_paths: Vec<RcStr>,
617 mut watcher_config: DiskWatcherConfig,
618 map: OperationVc<DiskFileSystemMap>,
619) -> Vc<DiskFileSystem> {
620 watcher_config.extended_batch_delay_matcher =
621 Some(ResolvedVc::upcast(NodeModulesPathMatcher.resolved_cell()));
622 DiskFileSystem::new_with_options(
623 name,
624 canonical_root.connect(),
625 denied_paths,
626 watcher_config,
627 map,
628 )
629}
630
631#[turbo_tasks::function(operation, root)]
632async fn project_root_path_operation(container: ResolvedVc<ProjectContainer>) -> Result<Vc<RcStr>> {
633 let container = container.await?;
634 let root_path = container
635 .options_state
636 .get()
637 .as_ref()
638 .context("Unexpected: ProjectContainer is uninitialized")?
639 .root_path
640 .clone();
641 Ok(Vc::cell(root_path))
642}
643
644#[turbo_tasks::function(operation, root)]
645pub(crate) async fn additional_root_path_operation(
646 container: ResolvedVc<ProjectContainer>,
647 key: RcStr,
648) -> Result<Vc<RcStr>> {
649 let container = container.await?;
650 let _guard = container.fs_map_init_lock.lock().await;
651 let roots = container.additional_roots_state.get();
652 let root = roots
653 .iter()
654 .find_map(|(name, root)| (name == &key).then_some(root))
655 .with_context(|| format!("Unexpected: additional root {key} is missing"))?;
656 Ok(Vc::cell(root.canonical_path.clone()))
657}
658
659#[turbo_tasks::function(operation, root)]
660async fn disk_file_system_map_operation(
661 container: ResolvedVc<ProjectContainer>,
662) -> Result<Vc<DiskFileSystemMap>> {
663 let (project_file_system, additional_file_systems) = {
664 let container = container.await?;
665 let _guard = container.fs_map_init_lock.lock().await;
666 let file_systems = container.file_systems_state.get();
667 let file_systems = file_systems
668 .as_ref()
669 .context("Unexpected: ProjectContainer is uninitialized")?;
670 (
671 file_systems.project_file_system,
672 container
673 .additional_roots_state
674 .get()
675 .iter()
676 .map(|(_, root)| root.file_system)
677 .collect::<Vec<_>>(),
678 )
679 };
680 let filesystems = iter::once(project_file_system)
681 .chain(additional_file_systems)
682 .map(async |operation| {
683 let fs = operation.connect().to_resolved().await?;
684 Ok((PathBuf::from(fs.await?.root()), fs))
685 })
686 .try_join()
687 .await?;
688 Ok(filesystems
689 .into_iter()
690 .collect::<DiskFileSystemMap>()
691 .cell())
692}
693
694enum EnvDiffType {
695 Added,
696 Removed,
697 Modified,
698}
699
700fn env_diff(
701 old: &[(RcStr, Option<RcStr>)],
702 new: &[(RcStr, Option<RcStr>)],
703) -> Vec<(RcStr, EnvDiffType)> {
704 let mut diffs = Vec::new();
705 let mut old_map: FxHashMap<_, _> = old.iter().cloned().collect();
706
707 for (key, new_value) in new.iter() {
708 match old_map.remove(key) {
709 Some(old_value) => {
710 if &old_value != new_value {
711 diffs.push((key.clone(), EnvDiffType::Modified));
712 }
713 }
714 None => {
715 diffs.push((key.clone(), EnvDiffType::Added));
716 }
717 }
718 }
719
720 for (key, _) in old.iter() {
721 if old_map.contains_key(key) {
722 diffs.push((key.clone(), EnvDiffType::Removed));
723 }
724 }
725
726 diffs
727}
728
729fn env_diff_report(old: &[(RcStr, Option<RcStr>)], new: &[(RcStr, Option<RcStr>)]) -> String {
730 use std::fmt::Write;
731
732 let diff = env_diff(old, new);
733
734 let mut report = String::new();
735 for (key, diff_type) in diff {
736 let symbol = match diff_type {
737 EnvDiffType::Added => "+",
738 EnvDiffType::Removed => "-",
739 EnvDiffType::Modified => "*",
740 };
741 if !report.is_empty() {
742 report.push_str(", ");
743 }
744 write!(report, "{}{}", symbol, key).unwrap();
745 }
746 report
747}
748
749fn define_env_diff_report(old: &DefineEnv, new: &DefineEnv) -> String {
750 use std::fmt::Write;
751
752 let mut report = String::new();
753 for (name, old, new) in [
754 ("client", &old.client, &new.client),
755 ("edge", &old.edge, &new.edge),
756 ("nodejs", &old.nodejs, &new.nodejs),
757 ] {
758 let diff = env_diff_report(old, new);
759 if !diff.is_empty() {
760 if !report.is_empty() {
761 report.push_str(", ");
762 }
763 write!(report, "{name}: {{ {diff} }}").unwrap();
764 }
765 }
766 report
767}
768
769impl ProjectContainer {
770 pub async fn initialize(
778 this_op: OperationVc<Self>,
779 options: ProjectOptions,
780 ) -> Result<Vec<ReadRef<PlainIssue>>> {
781 let this = this_op.read_strongly_consistent().await?;
782 let span = tracing::info_span!(
783 "initialize project",
784 project_name = %this.name,
785 version = options.next_version.as_str(),
786 node_version = options.current_node_js_version.as_str(),
787 os = std::env::consts::OS,
788 arch = std::env::consts::ARCH,
789 turbo_tasks_available_parallelism =
790 turbo_tasks::parallel::available_parallelism().map(|n| n.get()).unwrap_or(0),
791 std_thread_available_parallelism =
792 std::thread::available_parallelism().map(|n| n.get()).unwrap_or(0),
793 dev = options.dev,
794 env_diff = Empty
795 );
796 let span_clone = span.clone();
797 async move {
798 let container = this_op.resolve().strongly_consistent().await?;
799 prepare_project_container_state(container, options).await
800 }
801 .instrument(span_clone)
802 .await
803 }
804
805 pub async fn update(self: ResolvedVc<Self>, options: PartialProjectOptions) -> Result<()> {
806 let span = tracing::info_span!(
807 "update project options",
808 project_name = %self.await?.name,
809 env_diff = Empty
810 );
811 let span_clone = span.clone();
812 async move {
813 #[turbo_tasks::function(operation, root)]
819 fn project_container_operation_hack(
820 container: ResolvedVc<ProjectContainer>,
821 ) -> Vc<ProjectContainer> {
822 *container
823 }
824 let this = project_container_operation_hack(self)
825 .read_strongly_consistent()
826 .await?;
827 let PartialProjectOptions {
828 next_config,
829 env,
830 define_env,
831 dev,
832 encryption_key,
833 build_id,
834 preview_props,
835 browserslist_query,
836 no_mangling,
837 write_routes_hashes_manifest,
838 debug_build_paths,
839 } = options;
840
841 let mut new_options = this
844 .options_state
845 .get_untracked()
846 .clone()
847 .context("ProjectContainer need to be initialized with initialize()")?;
848 let old_define_env = new_options.define_env.clone();
849
850 if let Some(next_config) = next_config {
851 new_options.next_config = next_config;
852 }
853 if let Some(env) = env {
854 new_options.env = env;
855 }
856 if let Some(define_env) = define_env {
857 new_options.define_env = define_env;
858 }
859 if let Some(dev) = dev {
860 new_options.dev = dev;
861 }
862 if let Some(encryption_key) = encryption_key {
863 new_options.encryption_key = encryption_key;
864 }
865 if let Some(build_id) = build_id {
866 new_options.build_id = build_id;
867 }
868 if let Some(preview_props) = preview_props {
869 new_options.preview_props = preview_props;
870 }
871 if let Some(browserslist_query) = browserslist_query {
872 new_options.browserslist_query = browserslist_query;
873 }
874 if let Some(no_mangling) = no_mangling {
875 new_options.no_mangling = no_mangling;
876 }
877 if let Some(write_routes_hashes_manifest) = write_routes_hashes_manifest {
878 new_options.write_routes_hashes_manifest = write_routes_hashes_manifest;
879 }
880 if let Some(debug_build_paths) = debug_build_paths {
881 new_options.debug_build_paths = Some(debug_build_paths);
882 }
883
884 span.record(
885 "env_diff",
886 define_env_diff_report(&old_define_env, &new_options.define_env).as_str(),
887 );
888 this.options_state.set(Some(new_options));
889
890 Ok(())
891 }
892 .instrument(span_clone)
893 .await
894 }
895}
896
897#[turbo_tasks::value_impl]
898impl ProjectContainer {
899 #[turbo_tasks::function]
900 pub async fn project(&self) -> Result<Vc<Project>> {
901 let env_map: Vc<EnvMap>;
902 let next_config;
903 let define_env;
904 let root_path_str: RcStr;
905 let project_path;
906 let watch;
907 let dev;
908 let encryption_key;
909 let build_id;
910 let preview_props;
911 let browserslist_query;
912 let no_mangling;
913 let write_routes_hashes_manifest;
914 let current_node_js_version;
915 let debug_build_paths;
916 let deferred_entries;
917 let is_persistent_caching_enabled;
918 let server_hmr;
919 let project_file_system;
920 let output_file_system;
921 let additional_roots;
922 {
923 let options = self.options_state.get();
924 let options = options
925 .as_ref()
926 .context("ProjectContainer need to be initialized with initialize()")?;
927 let file_systems = self.file_systems_state.get();
928 let file_systems = file_systems
929 .as_ref()
930 .context("ProjectContainer need to be initialized with initialize()")?;
931 env_map = Vc::cell(options.env.iter().cloned().collect());
932 define_env = ProjectDefineEnv {
933 client: ResolvedVc::cell(options.define_env.client.iter().cloned().collect()),
934 edge: ResolvedVc::cell(options.define_env.edge.iter().cloned().collect()),
935 nodejs: ResolvedVc::cell(options.define_env.nodejs.iter().cloned().collect()),
936 }
937 .cell();
938 next_config = NextConfig::from_string(Vc::cell(options.next_config.clone()));
939 root_path_str = options.root_path.clone();
940 project_path = options.project_path.clone();
941 watch = options.watch;
942 dev = options.dev;
943 encryption_key = options.encryption_key.clone();
944 build_id = options.build_id.clone();
945 preview_props = options.preview_props.clone();
946 browserslist_query = options.browserslist_query.clone();
947 no_mangling = options.no_mangling;
948 write_routes_hashes_manifest = options.write_routes_hashes_manifest;
949 current_node_js_version = options.current_node_js_version.clone();
950 debug_build_paths = options.debug_build_paths.clone();
951 deferred_entries = options.deferred_entries.clone().unwrap_or_default();
952 is_persistent_caching_enabled = options.is_persistent_caching_enabled;
953 server_hmr = options.server_hmr;
954 project_file_system = file_systems.project_file_system;
955 output_file_system = file_systems.output_file_system;
956 additional_roots = self.additional_roots_state.get().iter().cloned().collect();
957 }
958
959 let root_path = ResolvedVc::cell(root_path_str);
960 let dist_dir = next_config.dist_dir().owned().await?;
961 let dist_dir_root = next_config.dist_dir_root().owned().await?;
962 Ok(Project {
963 root_path,
964 project_path,
965 watch,
966 next_config: next_config.to_resolved().await?,
967 dist_dir,
968 dist_dir_root,
969 env: ResolvedVc::upcast(env_map.to_resolved().await?),
970 define_env: define_env.to_resolved().await?,
971 browserslist_query,
972 mode: if dev {
973 NextMode::Development.resolved_cell()
974 } else {
975 NextMode::Build.resolved_cell()
976 },
977 versioned_content_map: self.versioned_content_map,
978 build_id,
979 encryption_key,
980 preview_props: ResolvedVc::cell(preview_props),
981 no_mangling,
982 write_routes_hashes_manifest,
983 current_node_js_version,
984 debug_build_paths,
985 deferred_entries,
986 is_persistent_caching_enabled,
987 server_hmr,
988 project_file_system,
989 output_file_system,
990 additional_roots,
991 }
992 .cell())
993 }
994
995 #[turbo_tasks::function]
997 pub fn entrypoints(self: Vc<Self>) -> Vc<Entrypoints> {
998 self.project().entrypoints()
999 }
1000
1001 #[turbo_tasks::function]
1003 pub fn hmr_chunk_names(self: Vc<Self>) -> Vc<Vec<RcStr>> {
1004 self.project().hmr_chunk_names()
1005 }
1006
1007 #[turbo_tasks::function]
1010 pub fn get_source_map(
1011 &self,
1012 file_path: FileSystemPath,
1013 section: Option<RcStr>,
1014 ) -> Vc<FileContent> {
1015 if let Some(map) = self.versioned_content_map {
1016 map.get_source_map(file_path, section)
1017 } else {
1018 FileContent::NotFound.cell()
1019 }
1020 }
1021}
1022
1023#[derive(Clone)]
1024#[turbo_tasks::value]
1025pub struct Project {
1026 root_path: ResolvedVc<RcStr>,
1030
1031 project_path: RcStr,
1035
1036 dist_dir: RcStr,
1040
1041 dist_dir_root: RcStr,
1045
1046 watch: WatchOptions,
1048
1049 next_config: ResolvedVc<NextConfig>,
1051
1052 env: ResolvedVc<Box<dyn ProcessEnv>>,
1054
1055 define_env: ResolvedVc<ProjectDefineEnv>,
1058
1059 browserslist_query: RcStr,
1061
1062 mode: ResolvedVc<NextMode>,
1063
1064 versioned_content_map: Option<ResolvedVc<VersionedContentMap>>,
1065
1066 build_id: RcStr,
1067
1068 encryption_key: RcStr,
1069
1070 preview_props: ResolvedVc<DraftModeOptionsValue>,
1071
1072 no_mangling: bool,
1076
1077 write_routes_hashes_manifest: bool,
1079
1080 current_node_js_version: RcStr,
1081
1082 debug_build_paths: Option<DebugBuildPaths>,
1085
1086 deferred_entries: Vec<RcStr>,
1088
1089 is_persistent_caching_enabled: bool,
1091
1092 server_hmr: bool,
1094
1095 project_file_system: OperationVc<DiskFileSystem>,
1096 output_file_system: OperationVc<DiskFileSystem>,
1097 #[bincode(with = "turbo_bincode::indexmap")]
1098 pub(crate) additional_roots: FxIndexMap<RcStr, AdditionalDiskFileSystem>,
1099}
1100
1101#[turbo_tasks::value]
1102pub struct ProjectDefineEnv {
1103 client: ResolvedVc<OptionEnvMap>,
1104 edge: ResolvedVc<OptionEnvMap>,
1105 nodejs: ResolvedVc<OptionEnvMap>,
1106}
1107
1108async fn import_meta_env_base_url(next_config: ResolvedVc<NextConfig>) -> Result<RcStr> {
1109 Ok(match &*next_config.base_path().await? {
1110 Some(base_path) => format!("{base_path}/").into(),
1111 None => rcstr!("/"),
1112 })
1113}
1114
1115#[turbo_tasks::value_impl]
1116impl ProjectDefineEnv {
1117 #[turbo_tasks::function]
1118 pub fn client(&self) -> Vc<OptionEnvMap> {
1119 *self.client
1120 }
1121
1122 #[turbo_tasks::function]
1123 pub fn edge(&self) -> Vc<OptionEnvMap> {
1124 *self.edge
1125 }
1126
1127 #[turbo_tasks::function]
1128 pub fn nodejs(&self) -> Vc<OptionEnvMap> {
1129 *self.nodejs
1130 }
1131}
1132
1133#[turbo_tasks::value(shared)]
1134struct ConflictIssue {
1135 path: FileSystemPath,
1136 title: ResolvedVc<StyledString>,
1137 description: ResolvedVc<StyledString>,
1138 severity: IssueSeverity,
1139}
1140
1141#[async_trait]
1142#[turbo_tasks::value_impl]
1143impl Issue for ConflictIssue {
1144 fn stage(&self) -> IssueStage {
1145 IssueStage::AppStructure
1146 }
1147
1148 fn severity(&self) -> IssueSeverity {
1149 self.severity
1150 }
1151
1152 async fn file_path(&self) -> Result<FileSystemPath> {
1153 Ok(self.path.clone())
1154 }
1155
1156 async fn title(&self) -> Result<StyledString> {
1157 self.title.owned().await
1158 }
1159
1160 async fn description(&self) -> Result<Option<StyledString>> {
1161 Ok(Some(self.description.owned().await?))
1162 }
1163}
1164
1165#[turbo_tasks::value_impl]
1166impl Project {
1167 #[turbo_tasks::function]
1168 pub async fn app_project(self: Vc<Self>) -> Result<Vc<OptionAppProject>> {
1169 let app_dir = find_app_dir(self.project_path().owned().await?).await?;
1170
1171 Ok(match &*app_dir {
1172 Some(app_dir) => Vc::cell(Some(
1173 AppProject::new(self, app_dir.clone()).to_resolved().await?,
1174 )),
1175 None => Vc::cell(None),
1176 })
1177 }
1178
1179 #[turbo_tasks::function]
1180 pub fn pages_project(self: Vc<Self>) -> Vc<PagesProject> {
1181 PagesProject::new(self)
1182 }
1183
1184 #[turbo_tasks::function]
1185 pub fn project_fs(&self) -> Result<Vc<DiskFileSystem>> {
1186 Ok(self.project_file_system.connect())
1187 }
1188
1189 #[turbo_tasks::function]
1190 pub fn client_fs(self: Vc<Self>) -> Vc<Box<dyn FileSystem>> {
1191 let virtual_fs = VirtualFileSystem::new_with_name(rcstr!("client-fs"));
1192 Vc::upcast(virtual_fs)
1193 }
1194
1195 #[turbo_tasks::function]
1196 pub fn output_fs(&self) -> Vc<DiskFileSystem> {
1197 self.output_file_system.connect()
1198 }
1199
1200 #[turbo_tasks::function]
1201 pub async fn node_root(self: Vc<Self>) -> Result<Vc<FileSystemPath>> {
1202 let this = self.await?;
1203 Ok(self
1204 .output_fs()
1205 .root()
1206 .await?
1207 .join(&this.project_path)?
1208 .join(&this.dist_dir)?
1209 .cell())
1210 }
1211
1212 #[turbo_tasks::function]
1213 pub fn client_root(self: Vc<Self>) -> Vc<FileSystemPath> {
1214 self.client_fs().root()
1215 }
1216
1217 #[turbo_tasks::function]
1218 pub fn project_root_path(self: Vc<Self>) -> Vc<FileSystemPath> {
1219 self.project_fs().root()
1220 }
1221
1222 #[turbo_tasks::function]
1223 pub async fn client_relative_path(self: Vc<Self>) -> Result<Vc<FileSystemPath>> {
1224 let next_config = self.next_config();
1225 Ok(self
1226 .client_root()
1227 .await?
1228 .join(&format!(
1229 "{}/_next",
1230 next_config
1231 .base_path()
1232 .await?
1233 .as_deref()
1234 .unwrap_or_default(),
1235 ))?
1236 .cell())
1237 }
1238
1239 #[turbo_tasks::function]
1243 pub async fn node_root_to_root_path(self: Vc<Self>) -> Result<Vc<RcStr>> {
1244 Ok(Vc::cell(
1245 self.node_root()
1246 .await?
1247 .get_relative_path_to(&*self.output_fs().root().await?)
1248 .context("Expected node root to be inside of output fs")?,
1249 ))
1250 }
1251
1252 #[turbo_tasks::function]
1253 pub async fn project_path(self: Vc<Self>) -> Result<Vc<FileSystemPath>> {
1254 let this = self.await?;
1255 let root = self.project_root_path().await?;
1256 Ok(root.join(&this.project_path)?.cell())
1257 }
1258
1259 #[turbo_tasks::function]
1260 pub(super) fn env(&self) -> Vc<Box<dyn ProcessEnv>> {
1261 *self.env
1262 }
1263
1264 #[turbo_tasks::function]
1265 pub async fn ci_has_next_support(&self) -> Result<Vc<bool>> {
1266 Ok(Vc::cell(
1267 self.env.read(rcstr!("NOW_BUILDER")).await?.is_some(),
1268 ))
1269 }
1270
1271 #[turbo_tasks::function]
1272 pub(super) fn current_node_js_version(&self) -> Vc<NodeJsVersion> {
1273 NodeJsVersion::Static(ResolvedVc::cell(self.current_node_js_version.clone())).cell()
1274 }
1275
1276 #[turbo_tasks::function]
1277 pub fn next_config(&self) -> Vc<NextConfig> {
1278 *self.next_config
1279 }
1280
1281 #[turbo_tasks::function]
1284 pub async fn issue_filter(self: Vc<Self>) -> Result<Vc<IssueFilter>> {
1285 let ignore_rules = self.next_config().turbopack_ignore_issue_rules().await?;
1286 Ok(IssueFilter::warnings_and_foreign_errors()
1287 .with_ignore_rules(ReadRef::into_owned(ignore_rules))
1288 .cell())
1289 }
1290
1291 #[turbo_tasks::function]
1292 pub(super) fn is_persistent_caching_enabled(&self) -> Vc<bool> {
1293 Vc::cell(self.is_persistent_caching_enabled)
1294 }
1295
1296 #[turbo_tasks::function]
1297 pub(super) fn next_mode(&self) -> Vc<NextMode> {
1298 *self.mode
1299 }
1300
1301 #[turbo_tasks::function]
1302 pub(super) fn is_watch_enabled(&self) -> Result<Vc<bool>> {
1303 Ok(Vc::cell(self.watch.enable))
1304 }
1305
1306 #[turbo_tasks::function]
1307 pub(super) fn should_write_routes_hashes_manifest(&self) -> Result<Vc<bool>> {
1308 Ok(Vc::cell(self.write_routes_hashes_manifest))
1309 }
1310
1311 #[turbo_tasks::function]
1312 pub(super) async fn should_write_nft_manifests(&self) -> Result<Vc<bool>> {
1313 Ok(Vc::cell(
1314 self.mode.await?.is_production()
1315 && *self.next_config.output().await? != Some(OutputType::Export),
1316 ))
1317 }
1318
1319 #[turbo_tasks::function]
1320 pub fn deferred_entries(&self) -> Vc<Vec<RcStr>> {
1321 Vc::cell(self.deferred_entries.clone())
1322 }
1323
1324 #[turbo_tasks::function]
1325 pub(super) async fn per_page_module_graph(&self) -> Result<Vc<bool>> {
1326 Ok(Vc::cell(*self.mode.await? == NextMode::Development))
1327 }
1328
1329 #[turbo_tasks::function]
1330 pub(super) fn encryption_key(&self) -> Vc<RcStr> {
1331 Vc::cell(self.encryption_key.clone())
1332 }
1333
1334 #[turbo_tasks::function]
1335 pub(super) fn no_mangling(&self) -> Vc<bool> {
1336 Vc::cell(self.no_mangling)
1337 }
1338
1339 #[turbo_tasks::function]
1340 pub(super) async fn execution_context(self: Vc<Self>) -> Result<Vc<ExecutionContext>> {
1341 let node_root = self.node_root().owned().await?;
1342 let next_mode = self.next_mode().await?;
1343 let strategy = *self
1344 .next_config()
1345 .turbopack_plugin_runtime_strategy()
1346 .await?;
1347 let node_backend = match strategy {
1348 #[cfg(feature = "worker_pool")]
1349 TurbopackPluginRuntimeStrategy::WorkerThreads => worker_threads_backend(),
1350 #[cfg(all(feature = "process_pool", not(target_family = "wasm")))]
1351 TurbopackPluginRuntimeStrategy::ChildProcesses => child_process_backend(),
1352 };
1353
1354 let node_execution_chunking_context = Vc::upcast(
1355 NodeJsChunkingContext::builder(
1356 self.project_root_path().owned().await?,
1357 node_root.join("build")?,
1358 self.node_root_to_root_path().owned().await?,
1359 node_root.join("build")?,
1360 node_root.join("build/chunks")?,
1361 node_root.join("build/assets")?,
1362 node_build_environment().to_resolved().await?,
1363 next_mode.runtime_type(),
1364 )
1365 .source_maps(*self.next_config().server_source_maps().await?)
1366 .shared_runtime_chunk(true)
1371 .build(),
1372 );
1373
1374 Ok(ExecutionContext::new(
1375 self.project_path().owned().await?,
1376 node_execution_chunking_context,
1377 self.env(),
1378 node_backend,
1379 ))
1380 }
1381
1382 #[turbo_tasks::function]
1383 pub(super) async fn client_compile_time_info(&self) -> Result<Vc<CompileTimeInfo>> {
1384 let next_mode = self.mode.await?;
1385 Ok(get_client_compile_time_info(
1386 self.browserslist_query.clone(),
1387 self.define_env.client(),
1388 self.next_config.report_system_env_inlining(),
1389 next_mode.is_development(),
1390 import_meta_env_base_url(self.next_config).await?,
1391 ))
1392 }
1393
1394 #[turbo_tasks::function]
1395 pub async fn get_all_endpoint_groups(
1396 self: Vc<Self>,
1397 app_dir_only: bool,
1398 ) -> Result<Vc<EndpointGroups>> {
1399 Ok(self.get_all_endpoint_groups_with_app_route_filter(app_dir_only, None))
1400 }
1401
1402 #[turbo_tasks::function]
1403 pub async fn get_all_endpoint_groups_with_app_route_filter(
1404 self: Vc<Self>,
1405 app_dir_only: bool,
1406 app_route_filter: Option<Vec<RcStr>>,
1407 ) -> Result<Vc<EndpointGroups>> {
1408 let mut endpoint_groups = Vec::new();
1409
1410 let entrypoints = self
1411 .entrypoints_with_app_route_filter(app_route_filter)
1412 .await?;
1413 let mut add_pages_entries = false;
1414
1415 if let Some(middleware) = &entrypoints.middleware {
1416 endpoint_groups.push((
1417 EndpointGroupKey::Middleware,
1418 EndpointGroup::from(middleware.endpoint),
1419 ));
1420 }
1421
1422 if let Some(instrumentation) = &entrypoints.instrumentation {
1423 endpoint_groups.push((
1424 EndpointGroupKey::Instrumentation,
1425 EndpointGroup::from(instrumentation.node_js),
1426 ));
1427 endpoint_groups.push((
1428 EndpointGroupKey::InstrumentationEdge,
1429 EndpointGroup::from(instrumentation.edge),
1430 ));
1431 }
1432
1433 for (key, route) in entrypoints.routes.iter() {
1434 match route {
1435 Route::Page {
1436 html_endpoint,
1437 data_endpoint,
1438 } => {
1439 if !app_dir_only {
1440 endpoint_groups.push((
1441 EndpointGroupKey::Route(key.clone()),
1442 EndpointGroup {
1443 primary: vec![EndpointGroupEntry {
1444 endpoint: *html_endpoint,
1445 sub_name: None,
1446 }],
1447 additional: data_endpoint
1449 .iter()
1450 .map(|endpoint| EndpointGroupEntry {
1451 endpoint: *endpoint,
1452 sub_name: None,
1453 })
1454 .collect(),
1455 },
1456 ));
1457 add_pages_entries = true;
1458 }
1459 }
1460 Route::PageApi { endpoint } => {
1461 if !app_dir_only {
1462 endpoint_groups.push((
1463 EndpointGroupKey::Route(key.clone()),
1464 EndpointGroup::from(*endpoint),
1465 ));
1466 add_pages_entries = true;
1467 }
1468 }
1469 Route::AppPage(page_routes) => {
1470 endpoint_groups.push((
1471 EndpointGroupKey::Route(key.clone()),
1472 EndpointGroup {
1473 primary: page_routes
1474 .iter()
1475 .map(|r| EndpointGroupEntry {
1476 endpoint: r.html_endpoint,
1477 sub_name: Some(r.original_name.clone()),
1478 })
1479 .collect(),
1480 additional: Vec::new(),
1481 },
1482 ));
1483 }
1484 Route::AppRoute {
1485 original_name: _,
1486 endpoint,
1487 ..
1488 } => {
1489 endpoint_groups.push((
1490 EndpointGroupKey::Route(key.clone()),
1491 EndpointGroup::from(*endpoint),
1492 ));
1493 }
1494 Route::Conflict => {
1495 tracing::info!("WARN: conflict");
1496 }
1497 }
1498 }
1499
1500 if add_pages_entries {
1501 endpoint_groups.push((
1502 EndpointGroupKey::PagesError,
1503 EndpointGroup::from(entrypoints.pages_error_endpoint),
1504 ));
1505 endpoint_groups.push((
1506 EndpointGroupKey::PagesApp,
1507 EndpointGroup::from(entrypoints.pages_app_endpoint),
1508 ));
1509 endpoint_groups.push((
1510 EndpointGroupKey::PagesDocument,
1511 EndpointGroup::from(entrypoints.pages_document_endpoint),
1512 ));
1513 }
1514
1515 Ok(Vc::cell(endpoint_groups))
1516 }
1517
1518 #[turbo_tasks::function]
1519 pub async fn get_all_endpoints(self: Vc<Self>, app_dir_only: bool) -> Result<Vc<Endpoints>> {
1520 let mut endpoints = Vec::new();
1521 for (_key, group) in self.get_all_endpoint_groups(app_dir_only).await?.iter() {
1522 for entry in group.primary.iter() {
1523 endpoints.push(entry.endpoint);
1524 }
1525 for entry in group.additional.iter() {
1526 endpoints.push(entry.endpoint);
1527 }
1528 }
1529
1530 Ok(Vc::cell(endpoints))
1531 }
1532
1533 #[turbo_tasks::function]
1534 pub async fn get_all_entries(self: Vc<Self>) -> Result<Vc<GraphEntries>> {
1535 let endpoint_entries = self
1536 .get_all_endpoints(false)
1537 .await?
1538 .iter()
1539 .map(|endpoint| endpoint.entries().owned())
1540 .try_join()
1541 .await?;
1542
1543 let result = GraphEntries::concatenate(
1544 endpoint_entries
1545 .into_iter()
1546 .chain(iter::once(self.client_main_modules().owned().await?))
1547 .chain(iter::once(GraphEntries::new(
1548 vec![],
1549 self.pages_traced_modules().owned().await?,
1553 ))),
1554 );
1555
1556 Ok(result.cell())
1557 }
1558
1559 #[turbo_tasks::function]
1560 pub async fn get_all_additional_entries(
1561 self: Vc<Self>,
1562 graphs: Vc<ModuleGraph>,
1563 ) -> Result<Vc<GraphEntries>> {
1564 let result = GraphEntries::concatenate(
1565 self.get_all_endpoints(false)
1566 .await?
1567 .iter()
1568 .map(|endpoint| endpoint.additional_entries(graphs).owned())
1569 .try_join()
1570 .await?,
1571 );
1572 Ok(result.cell())
1573 }
1574
1575 #[turbo_tasks::function]
1576 pub async fn module_graph(
1577 self: Vc<Self>,
1578 entry: ResolvedVc<Box<dyn Module>>,
1579 ) -> Result<Vc<ModuleGraph>> {
1580 Ok(if *self.per_page_module_graph().await? {
1581 ModuleGraph::from_graphs(
1582 vec![SingleModuleGraph::new_with_entry(
1583 ChunkGroupEntry::Entry {
1584 modules: vec![entry],
1585 heuristics: EntryHeuristics::default(),
1586 },
1587 *self.should_write_nft_manifests().await?,
1588 self.next_mode().await?.is_production(),
1589 )],
1590 None,
1591 )
1592 .connect()
1593 } else {
1594 *self.whole_app_module_graphs().await?.full
1595 })
1596 }
1597
1598 #[turbo_tasks::function]
1599 pub async fn module_graph_for_modules(
1600 self: Vc<Self>,
1601 evaluatable_assets: Vc<EvaluatableAssets>,
1602 ) -> Result<Vc<ModuleGraph>> {
1603 Ok(if *self.per_page_module_graph().await? {
1604 let entries = evaluatable_assets
1605 .await?
1606 .iter()
1607 .copied()
1608 .map(ResolvedVc::upcast)
1609 .collect();
1610 ModuleGraph::from_graphs(
1611 vec![SingleModuleGraph::new_with_entries(
1612 GraphEntries::from_chunk_groups(vec![ChunkGroupEntry::Entry {
1613 modules: entries,
1614 heuristics: EntryHeuristics::default(),
1615 }])
1616 .resolved_cell(),
1617 *self.should_write_nft_manifests().await?,
1618 self.next_mode().await?.is_production(),
1619 )],
1620 None,
1621 )
1622 .connect()
1623 } else {
1624 *self.whole_app_module_graphs().await?.full
1625 })
1626 }
1627
1628 #[turbo_tasks::function]
1633 pub async fn whole_app_module_graphs_without_dropping_issues(
1634 self: ResolvedVc<Self>,
1635 ) -> Result<Vc<BaseAndFullModuleGraph>> {
1636 let module_graphs_op = whole_app_module_graph_operation(self);
1637 let module_graphs_vc = module_graphs_op.connect();
1638 scale_down_node_pool(self).await?;
1639 Ok(module_graphs_vc)
1640 }
1641
1642 #[turbo_tasks::function(root)]
1645 pub async fn whole_app_module_graphs(
1646 self: ResolvedVc<Self>,
1647 ) -> Result<Vc<BaseAndFullModuleGraph>> {
1648 let module_graphs_op = whole_app_module_graph_operation(self);
1649 let module_graphs_vc = if self.next_mode().await?.is_production() {
1650 module_graphs_op.connect()
1651 } else {
1652 let vc = module_graphs_op.resolve().strongly_consistent().await?;
1653 module_graphs_op.drop_issues();
1654 *vc
1655 };
1656 scale_down_node_pool(self).await?;
1657 Ok(module_graphs_vc)
1658 }
1659
1660 #[turbo_tasks::function]
1661 pub(super) async fn server_compile_time_info(self: Vc<Self>) -> Result<Vc<CompileTimeInfo>> {
1662 let this = self.await?;
1663 Ok(get_server_compile_time_info(
1664 self.project_path(),
1666 this.define_env.nodejs(),
1667 self.current_node_js_version(),
1668 this.next_config.report_system_env_inlining(),
1669 this.server_hmr,
1670 import_meta_env_base_url(this.next_config).await?,
1671 ))
1672 }
1673
1674 #[turbo_tasks::function]
1675 pub(super) async fn edge_compile_time_info(self: Vc<Self>) -> Result<Vc<CompileTimeInfo>> {
1676 let this = self.await?;
1677 Ok(get_edge_compile_time_info(
1678 self.project_path().owned().await?,
1679 this.define_env.edge(),
1680 self.current_node_js_version(),
1681 this.next_config.report_system_env_inlining(),
1682 import_meta_env_base_url(this.next_config).await?,
1683 ))
1684 }
1685
1686 #[turbo_tasks::function]
1687 pub(super) async fn edge_env(&self) -> Result<Vc<EnvMap>> {
1688 let preview_props = self.preview_props.await?;
1689 let edge_env = fxindexmap! {
1690 rcstr!("__NEXT_BUILD_ID") => self.build_id.clone(),
1691 rcstr!("NEXT_SERVER_ACTIONS_ENCRYPTION_KEY") => self.encryption_key.clone(),
1692 rcstr!("__NEXT_PREVIEW_MODE_ID") => preview_props.preview_mode_id.clone(),
1693 rcstr!("__NEXT_PREVIEW_MODE_ENCRYPTION_KEY") => preview_props.preview_mode_encryption_key.clone(),
1694 rcstr!("__NEXT_PREVIEW_MODE_SIGNING_KEY") => preview_props.preview_mode_signing_key.clone(),
1695 };
1696 Ok(Vc::cell(edge_env))
1697 }
1698
1699 #[turbo_tasks::function]
1700 pub(super) async fn client_chunking_context(
1701 self: Vc<Self>,
1702 ) -> Result<Vc<Box<dyn ChunkingContext>>> {
1703 let css_url_suffix = self.next_config().asset_suffix_path();
1704 let turbopack_chunking = self.next_config().turbopack_chunking().await?;
1705 Ok(get_client_chunking_context(ClientChunkingContextOptions {
1706 mode: self.next_mode(),
1707 root_path: self.project_root_path().owned().await?,
1708 client_root: self.client_relative_path().owned().await?,
1709 client_root_to_root_path: rcstr!("/ROOT"),
1710 client_static_folder_name: self
1711 .next_config()
1712 .client_static_folder_name()
1713 .owned()
1714 .await?,
1715 asset_prefix: self.next_config().computed_asset_prefix(),
1716 service_worker_scope_base_path: self.next_config().base_path(),
1717 environment: self.client_compile_time_info().environment(),
1718 module_id_strategy: self.module_ids(),
1719 export_usage: self.export_usage(),
1720 unused_references: self.unused_references(),
1721 minify: self.next_config().turbo_client_minify(self.next_mode()),
1722 source_maps: self.next_config().client_source_maps(self.next_mode()),
1723 no_mangling: self.no_mangling(),
1724 scope_hoisting: self.next_config().turbo_scope_hoisting(self.next_mode()),
1725 nested_async_chunking: self
1726 .next_config()
1727 .turbo_nested_async_chunking(self.next_mode(), true),
1728 shared_runtime: self.next_config().turbo_shared_runtime(self.next_mode()),
1729 per_page_module_graph: self.per_page_module_graph(),
1730 lazy_dynamic_imports: self
1731 .next_config()
1732 .turbopack_lazy_dynamic_imports(*self.next_mode().await?),
1733 debug_ids: self.next_config().turbopack_debug_ids(),
1734 worker_asset_prefix: self.next_config().turbopack_worker_asset_prefix(),
1735 should_use_absolute_url_references: self.next_config().inline_css(),
1736 css_url_suffix,
1737 hash_salt: self.next_config().output_hash_salt().to_resolved().await?,
1738 cross_origin: self.next_config().cross_origin(),
1739 chunk_loading_global: self.next_config().turbopack_chunk_loading_global(),
1740 style_groups_algorithm: self.next_config().css_chunking().owned().await?,
1741 chunking_first_page_load_priority: turbopack_chunking.first_page_load_priority,
1742 chunking_priority_boost_percent: turbopack_chunking.priority_boost_percent,
1743 chunking_request_cost: turbopack_chunking.request_cost,
1744 chunking_min_chunk_size: turbopack_chunking.min_chunk_size,
1745 chunking_max_chunk_count_per_group: turbopack_chunking.max_chunk_count_per_group,
1746 chunking_max_merge_chunk_size: turbopack_chunking.max_merge_chunk_size,
1747 chunking_min_component_chunk_size: turbopack_chunking.min_component_chunk_size,
1748 generate_component_chunks: self.next_config().turbopack_generate_component_chunks(),
1749 }))
1750 }
1751
1752 #[turbo_tasks::function]
1753 pub(super) async fn service_worker_chunking_context(
1754 self: Vc<Self>,
1755 ) -> Result<Vc<Box<dyn ChunkingContext>>> {
1756 Ok(get_service_worker_chunking_context(
1757 ServiceWorkerChunkingContextOptions {
1758 mode: self.next_mode(),
1759 root_path: self.project_root_path().owned().await?,
1760 output_root: self.node_root().owned().await?,
1761 output_root_to_root_path: self.node_root_to_root_path().owned().await?,
1762 environment: self.client_compile_time_info().environment(),
1763 minify: self.next_config().turbo_client_minify(self.next_mode()),
1764 source_maps: self.next_config().client_source_maps(self.next_mode()),
1765 no_mangling: self.no_mangling(),
1766 hash_salt: self.next_config().output_hash_salt().to_resolved().await?,
1767 },
1768 ))
1769 }
1770
1771 #[turbo_tasks::function]
1772 pub(super) async fn service_worker_asset_context(
1773 self: Vc<Self>,
1774 ) -> Result<Vc<Box<dyn AssetContext>>> {
1775 Ok(Vc::upcast(ModuleAssetContext::new(
1776 TransitionOptions::default().cell(),
1777 self.client_compile_time_info(),
1778 get_client_module_options_context(
1779 self.project_path().owned().await?,
1780 self.execution_context(),
1781 self.client_compile_time_info().environment(),
1782 ClientContextType::Other,
1783 self.next_mode(),
1784 self.next_config(),
1785 self.encryption_key(),
1786 ),
1787 get_client_resolve_options_context(
1788 self.project_path().owned().await?,
1789 ClientContextType::Other,
1790 self.next_mode(),
1791 self.next_config(),
1792 self.execution_context(),
1793 ),
1794 Layer::new_with_user_friendly_name(rcstr!("service-worker"), rcstr!("Service Worker")),
1795 )))
1796 }
1797
1798 #[turbo_tasks::function]
1801 pub(super) async fn server_chunking_context(
1802 self: Vc<Self>,
1803 client_assets: bool,
1804 supports_lazy_dynamic_imports: bool,
1805 ) -> Result<Vc<NodeJsChunkingContext>> {
1806 let css_url_suffix = self.next_config().asset_suffix_path();
1807 let options = ServerChunkingContextOptions {
1808 mode: self.next_mode(),
1809 root_path: self.project_root_path().owned().await?,
1810 node_root: self.node_root().owned().await?,
1811 node_root_to_root_path: self.node_root_to_root_path().owned().await?,
1812 environment: self.server_compile_time_info().environment(),
1813 module_id_strategy: self.module_ids(),
1814 export_usage: self.export_usage(),
1815 unused_references: self.unused_references(),
1816 minify: self.next_config().turbo_server_minify(self.next_mode()),
1817 source_maps: self.next_config().server_source_maps(),
1818 no_mangling: self.no_mangling(),
1819 scope_hoisting: self.next_config().turbo_scope_hoisting(self.next_mode()),
1820 nested_async_chunking: self
1821 .next_config()
1822 .turbo_nested_async_chunking(self.next_mode(), false),
1823 debug_ids: self.next_config().turbopack_debug_ids(),
1824 client_root: self.client_relative_path().owned().await?,
1825 client_static_folder_name: self
1826 .next_config()
1827 .client_static_folder_name()
1828 .owned()
1829 .await?,
1830 asset_prefix: self.next_config().computed_asset_prefix().owned().await?,
1831 css_url_suffix,
1832 hash_salt: self.next_config().output_hash_salt().to_resolved().await?,
1833 style_groups_algorithm: self.next_config().css_chunking().owned().await?,
1834 per_page_module_graph: self.per_page_module_graph(),
1835 lazy_dynamic_imports: supports_lazy_dynamic_imports
1836 && *self
1837 .next_config()
1838 .turbopack_lazy_dynamic_imports_ssr(*self.next_mode().await?)
1839 .await?,
1840 };
1841 Ok(if client_assets {
1842 get_server_chunking_context_with_client_assets(options)
1843 } else {
1844 get_server_chunking_context(options)
1845 })
1846 }
1847
1848 #[turbo_tasks::function]
1849 pub(super) async fn edge_chunking_context(
1850 self: Vc<Self>,
1851 client_assets: bool,
1852 ) -> Result<Vc<Box<dyn ChunkingContext>>> {
1853 let css_url_suffix = self.next_config().asset_suffix_path();
1854 let options = EdgeChunkingContextOptions {
1855 mode: self.next_mode(),
1856 root_path: self.project_root_path().owned().await?,
1857 node_root: self.node_root().owned().await?,
1858 output_root_to_root_path: self.node_root_to_root_path(),
1859 environment: self.edge_compile_time_info().environment(),
1860 module_id_strategy: self.module_ids(),
1861 export_usage: self.export_usage(),
1862 unused_references: self.unused_references(),
1863 turbo_minify: self.next_config().turbo_edge_minify(self.next_mode()),
1864 turbo_source_maps: self.next_config().server_source_maps(),
1865 no_mangling: self.no_mangling(),
1866 scope_hoisting: self.next_config().turbo_scope_hoisting(self.next_mode()),
1867 nested_async_chunking: self
1868 .next_config()
1869 .turbo_nested_async_chunking(self.next_mode(), false),
1870 client_root: self.client_relative_path().owned().await?,
1871 client_static_folder_name: self
1872 .next_config()
1873 .client_static_folder_name()
1874 .owned()
1875 .await?,
1876 asset_prefix: self.next_config().computed_asset_prefix().owned().await?,
1877 css_url_suffix,
1878 hash_salt: self.next_config().output_hash_salt().to_resolved().await?,
1879 cross_origin: self.next_config().cross_origin(),
1880 style_groups_algorithm: self.next_config().css_chunking().owned().await?,
1881 };
1882 Ok(if client_assets {
1883 get_edge_chunking_context_with_client_assets(options)
1884 } else {
1885 get_edge_chunking_context(options)
1886 })
1887 }
1888
1889 #[turbo_tasks::function]
1890 pub(super) fn runtime_chunking_context(
1891 self: Vc<Self>,
1892 client_assets: bool,
1893 runtime: NextRuntime,
1894 ) -> Vc<Box<dyn ChunkingContext>> {
1895 match runtime {
1896 NextRuntime::Edge => self.edge_chunking_context(client_assets),
1897 NextRuntime::NodeJs => Vc::upcast(self.server_chunking_context(
1898 client_assets,
1899 true, )),
1901 }
1902 }
1903
1904 #[turbo_tasks::function]
1922 pub async fn project_feature_usage(
1923 self: ResolvedVc<Self>,
1924 ) -> Result<Vc<ProjectFeatureUsageSummary>> {
1925 if !self.next_mode().await?.is_production() {
1926 bail!("project_feature_usage() may only be called during `next build`");
1927 }
1928
1929 static FEATURE_MODULE_PATH_SUFFIXES: &[(&str, &str)] = &[
1943 ("next/image", "/next/image.js"),
1944 ("next/image", "/next/dist/api/image.js"),
1945 ("next/image", "/next/dist/esm/api/image.js"),
1946 ("next/image", "/next/dist/client/image-component.js"),
1950 ("next/image", "/next/dist/esm/client/image-component.js"),
1951 (
1952 "next/image",
1953 "/next/dist/shared/lib/image-external-get-image-props.js",
1954 ),
1955 (
1956 "next/image",
1957 "/next/dist/esm/shared/lib/image-external-get-image-props.js",
1958 ),
1959 ("next/future/image", "/next/future/image.js"),
1960 ("next/legacy/image", "/next/legacy/image.js"),
1961 ("next/script", "/next/script.js"),
1962 ("next/dynamic", "/next/dynamic.js"),
1963 ("next/font/google", "/next/font/google/target.css"),
1964 ("next/font/local", "/next/font/local/target.css"),
1965 ("@next/font/google", "/@next/font/google/target.css"),
1966 ("@next/font/local", "/@next/font/local/target.css"),
1967 ];
1968
1969 let config = self.next_config();
1972 let compiler_options = config.compiler().await?;
1973 let mut features: Vec<(RcStr, u32)> = vec![
1974 (
1977 format!("swc/target/{}", env!("VERGEN_CARGO_TARGET_TRIPLE")).into(),
1978 1,
1979 ),
1980 (
1981 rcstr!("skipProxyUrlNormalize"),
1982 (*config.skip_proxy_url_normalize().await?) as u32,
1983 ),
1984 (
1985 rcstr!("skipTrailingSlashRedirect"),
1986 (*config.skip_trailing_slash_redirect().await?) as u32,
1987 ),
1988 (
1989 rcstr!("modularizeImports"),
1990 !config.modularize_imports().await?.is_empty() as u32,
1991 ),
1992 (
1993 rcstr!("transpilePackages"),
1994 !config.transpile_packages().await?.is_empty() as u32,
1995 ),
1996 (rcstr!("swcRelay"), compiler_options.relay.is_some() as u32),
1997 (
1998 rcstr!("swcStyledComponents"),
1999 compiler_options
2000 .styled_components
2001 .as_ref()
2002 .is_some_and(|sc| sc.is_enabled()) as u32,
2003 ),
2004 (
2005 rcstr!("swcReactRemoveProperties"),
2006 compiler_options
2007 .react_remove_properties
2008 .as_ref()
2009 .is_some_and(|rc| rc.is_enabled()) as u32,
2010 ),
2011 (
2012 rcstr!("swcRemoveConsole"),
2013 compiler_options
2014 .remove_console
2015 .as_ref()
2016 .is_some_and(|rc| rc.is_enabled()) as u32,
2017 ),
2018 (
2019 rcstr!("swcEmotion"),
2020 compiler_options
2021 .emotion
2022 .as_ref()
2023 .is_some_and(|e| e.is_enabled()) as u32,
2024 ),
2025 ];
2026
2027 let module_graph = self.whole_app_module_graphs().await?.full.await?;
2032
2033 let matching: FxHashMap<ResolvedVc<Box<dyn Module>>, &'static str> = module_graph
2034 .iter_nodes()
2035 .map(async |node| {
2036 let ident = node.ident().await?;
2037 let path = &ident.path.path;
2038 for &(feature, suffix) in FEATURE_MODULE_PATH_SUFFIXES {
2039 if path.ends_with(suffix) {
2040 return Ok(Some((node, feature)));
2041 }
2042 }
2043 Ok(None)
2044 })
2045 .try_flat_join()
2046 .await?
2047 .into_iter()
2048 .collect();
2049
2050 let mut pairs: FxHashSet<(&'static str, ResolvedVc<Box<dyn Module>>)> =
2058 FxHashSet::default();
2059 module_graph.traverse_edges_unordered(|parent, node| {
2060 if let Some((parent_node, _)) = parent
2061 && let Some(&feature) = matching.get(&node)
2062 && matching.get(&parent_node) != Some(&feature)
2065 {
2066 pairs.insert((feature, parent_node));
2067 }
2068 Ok(())
2069 })?;
2070
2071 let parent_source_keys = pairs
2076 .into_iter()
2077 .map(async |(feature, parent)| {
2078 let ident = parent.ident().await?;
2079 let key = (
2080 ident.path.path.clone(),
2081 ident.query.clone(),
2082 ident.fragment.clone(),
2083 );
2084 Ok((feature, key))
2085 })
2086 .try_join()
2087 .await?;
2088
2089 let mut importers: FxHashMap<&'static str, FxHashSet<(RcStr, RcStr, RcStr)>> =
2090 FxHashMap::default();
2091 for (feature, key) in parent_source_keys {
2092 importers.entry(feature).or_default().insert(key);
2093 }
2094 for (feature, unique_sources) in importers {
2095 features.push((RcStr::from(feature), unique_sources.len() as u32));
2096 }
2097
2098 features.sort_by(|a, b| a.0.cmp(&b.0));
2099 Ok(ProjectFeatureUsageSummary { features }.cell())
2100 }
2101
2102 #[turbo_tasks::function]
2105 pub async fn entrypoints(self: Vc<Self>) -> Result<Vc<Entrypoints>> {
2106 Ok(self.entrypoints_with_app_route_filter(None))
2107 }
2108
2109 #[turbo_tasks::function]
2110 pub async fn entrypoints_with_app_route_filter(
2111 self: Vc<Self>,
2112 app_route_filter: Option<Vec<RcStr>>,
2113 ) -> Result<Vc<Entrypoints>> {
2114 let this = self.await?;
2115 let mut routes = FxIndexMap::default();
2116 let app_project = self.app_project();
2117 let pages_project = self.pages_project();
2118
2119 let debug_build_paths_route_keys = this
2121 .debug_build_paths
2122 .as_ref()
2123 .map(DebugBuildPathsRouteKeys::from_debug_build_paths)
2124 .transpose()?;
2125
2126 if let Some(app_project) = &*app_project.await? {
2127 let app_routes = app_project.routes_with_filter(app_route_filter);
2128 routes.extend(
2129 app_routes
2130 .await?
2131 .iter()
2132 .filter(|(k, _)| {
2133 debug_build_paths_route_keys
2134 .as_ref()
2135 .is_none_or(|keys| keys.should_include_app_route(k))
2136 })
2137 .map(|(k, v)| (k.clone(), v.clone())),
2138 );
2139 }
2140
2141 for (pathname, page_route) in &pages_project.routes().await? {
2142 if debug_build_paths_route_keys
2143 .as_ref()
2144 .is_some_and(|keys| !keys.should_include_pages_route(pathname))
2145 {
2146 continue;
2147 }
2148
2149 match routes.entry(pathname.clone()) {
2150 Entry::Occupied(mut entry) => {
2151 ConflictIssue {
2152 path: self.project_path().owned().await?,
2153 title: StyledString::Text(
2154 format!("App Router and Pages Router both match path: {pathname}")
2155 .into(),
2156 )
2157 .resolved_cell(),
2158 description: StyledString::Text(
2159 "Next.js does not support having both App Router and Pages Router \
2160 routes matching the same path. Please remove one of the conflicting \
2161 routes."
2162 .into(),
2163 )
2164 .resolved_cell(),
2165 severity: IssueSeverity::Error,
2166 }
2167 .resolved_cell()
2168 .emit();
2169 *entry.get_mut() = Route::Conflict;
2170 }
2171 Entry::Vacant(entry) => {
2172 entry.insert(page_route.clone());
2173 }
2174 }
2175 }
2176
2177 let pages_document_endpoint = self
2178 .pages_project()
2179 .document_endpoint()
2180 .to_resolved()
2181 .await?;
2182 let pages_app_endpoint = self.pages_project().app_endpoint().to_resolved().await?;
2183 let pages_error_endpoint = self.pages_project().error_endpoint().to_resolved().await?;
2184
2185 let middleware = self.find_middleware();
2186 let middleware = if let FindContextFileResult::Found(fs_path, _) = &*middleware.await? {
2187 let is_proxy = convention_file_base_name(fs_path.file_name()) == "proxy";
2188 Some(Middleware {
2189 endpoint: self.middleware_endpoint().to_resolved().await?,
2190 is_proxy,
2191 })
2192 } else {
2193 None
2194 };
2195
2196 let instrumentation = self.find_instrumentation();
2197 let instrumentation = if let FindContextFileResult::Found(..) = *instrumentation.await? {
2198 Some(Instrumentation {
2199 node_js: self.instrumentation_endpoint(false).to_resolved().await?,
2200 edge: self.instrumentation_endpoint(true).to_resolved().await?,
2201 })
2202 } else {
2203 None
2204 };
2205
2206 Ok(Entrypoints {
2207 routes,
2208 middleware,
2209 instrumentation,
2210 pages_document_endpoint,
2211 pages_app_endpoint,
2212 pages_error_endpoint,
2213 }
2214 .cell())
2215 }
2216
2217 #[turbo_tasks::function]
2218 async fn edge_middleware_context(self: Vc<Self>) -> Result<Vc<Box<dyn AssetContext>>> {
2219 let mut transitions = vec![];
2220
2221 let app_dir = find_app_dir(self.project_path().owned().await?)
2222 .owned()
2223 .await?;
2224 let app_project = *self.app_project().await?;
2225
2226 let ecmascript_client_reference_transition_name =
2227 app_project.map(|_| AppProject::client_transition_name());
2228
2229 if let Some(app_project) = app_project {
2230 transitions.push((
2231 AppProject::client_transition_name(),
2232 app_project
2233 .edge_ecmascript_client_reference_transition()
2234 .to_resolved()
2235 .await?,
2236 ));
2237 }
2238
2239 Ok(Vc::upcast(ModuleAssetContext::new(
2240 TransitionOptions {
2241 named_transitions: transitions.clone().into_iter().collect(),
2242 ..Default::default()
2243 }
2244 .cell(),
2245 self.edge_compile_time_info(),
2246 get_server_module_options_context(
2247 self.project_path().owned().await?,
2248 self.execution_context(),
2249 ServerContextType::Middleware {
2250 app_dir: app_dir.clone(),
2251 ecmascript_client_reference_transition_name:
2252 ecmascript_client_reference_transition_name.clone(),
2253 },
2254 self.next_mode(),
2255 self.next_config(),
2256 NextRuntime::Edge,
2257 self.encryption_key(),
2258 self.edge_compile_time_info().environment(),
2259 self.client_compile_time_info().environment(),
2260 false,
2262 ),
2263 get_edge_resolve_options_context(
2264 self.project_path().owned().await?,
2265 ServerContextType::Middleware {
2266 app_dir: app_dir.clone(),
2267 ecmascript_client_reference_transition_name:
2268 ecmascript_client_reference_transition_name.clone(),
2269 },
2270 self.next_mode(),
2271 self.next_config(),
2272 self.execution_context(),
2273 None, ),
2275 Layer::new_with_user_friendly_name(
2276 rcstr!("middleware-edge"),
2277 rcstr!("Edge Middleware"),
2278 ),
2279 )))
2280 }
2281
2282 #[turbo_tasks::function]
2283 async fn node_middleware_context(self: Vc<Self>) -> Result<Vc<Box<dyn AssetContext>>> {
2284 let mut transitions = vec![];
2285
2286 let app_dir = find_app_dir(self.project_path().owned().await?)
2287 .owned()
2288 .await?;
2289 let app_project = *self.app_project().await?;
2290
2291 let ecmascript_client_reference_transition_name =
2292 app_project.map(|_| AppProject::client_transition_name());
2293
2294 if let Some(app_project) = app_project {
2295 transitions.push((
2296 AppProject::client_transition_name(),
2297 app_project
2298 .edge_ecmascript_client_reference_transition()
2299 .to_resolved()
2300 .await?,
2301 ));
2302 }
2303
2304 Ok(Vc::upcast(ModuleAssetContext::new(
2305 TransitionOptions {
2306 named_transitions: transitions.clone().into_iter().collect(),
2307 ..Default::default()
2308 }
2309 .cell(),
2310 self.server_compile_time_info(),
2311 get_server_module_options_context(
2312 self.project_path().owned().await?,
2313 self.execution_context(),
2314 ServerContextType::Middleware {
2315 app_dir: app_dir.clone(),
2316 ecmascript_client_reference_transition_name:
2317 ecmascript_client_reference_transition_name.clone(),
2318 },
2319 self.next_mode(),
2320 self.next_config(),
2321 NextRuntime::NodeJs,
2322 self.encryption_key(),
2323 self.server_compile_time_info().environment(),
2324 self.client_compile_time_info().environment(),
2325 *self.should_write_nft_manifests().await?,
2326 ),
2327 get_server_resolve_options_context(
2328 self.project_path().owned().await?,
2329 ServerContextType::Middleware {
2330 app_dir: app_dir.clone(),
2331 ecmascript_client_reference_transition_name,
2332 },
2333 self.next_mode(),
2334 self.next_config(),
2335 self.execution_context(),
2336 None, ),
2338 Layer::new_with_user_friendly_name(rcstr!("middleware"), rcstr!("Middleware")),
2339 )))
2340 }
2341
2342 #[turbo_tasks::function]
2343 async fn find_middleware(self: Vc<Self>) -> Result<Vc<FindContextFileResult>> {
2344 Ok(find_context_file(
2345 self.project_path().owned().await?,
2346 middleware_files(self.next_config().page_extensions()),
2347 false,
2349 ))
2350 }
2351
2352 #[turbo_tasks::function]
2353 async fn middleware_endpoint(self: Vc<Self>) -> Result<Vc<Box<dyn Endpoint>>> {
2354 let middleware = self.find_middleware();
2355 let FindContextFileResult::Found(fs_path, _) = &*middleware.await? else {
2356 return Ok(Vc::upcast(EmptyEndpoint::new(self)));
2357 };
2358 let source = Vc::upcast(FileSource::new(fs_path.clone()));
2359 let app_dir = find_app_dir(self.project_path().owned().await?)
2360 .owned()
2361 .await?;
2362 let ecmascript_client_reference_transition_name = (*self.app_project().await?)
2363 .as_ref()
2364 .map(|_| AppProject::client_transition_name());
2365
2366 let is_proxy = convention_file_base_name(fs_path.file_name()) == "proxy";
2367 let config = parse_segment_config_from_source(
2368 source,
2369 if is_proxy {
2370 ParseSegmentMode::Proxy
2371 } else {
2372 ParseSegmentMode::Base
2373 },
2374 );
2375 let runtime = config.await?.runtime.unwrap_or(if is_proxy {
2376 NextRuntime::NodeJs
2377 } else {
2378 NextRuntime::Edge
2379 });
2380
2381 let middleware_asset_context = match runtime {
2382 NextRuntime::NodeJs => self.node_middleware_context(),
2383 NextRuntime::Edge => self.edge_middleware_context(),
2384 };
2385
2386 Ok(Vc::upcast(MiddlewareEndpoint::new(
2387 self,
2388 middleware_asset_context,
2389 source,
2390 app_dir.clone(),
2391 ecmascript_client_reference_transition_name,
2392 config,
2393 runtime,
2394 )))
2395 }
2396
2397 #[turbo_tasks::function]
2398 async fn node_instrumentation_context(self: Vc<Self>) -> Result<Vc<Box<dyn AssetContext>>> {
2399 let mut transitions = vec![];
2400
2401 let app_dir = find_app_dir(self.project_path().owned().await?)
2402 .owned()
2403 .await?;
2404 let app_project = &*self.app_project().await?;
2405
2406 let ecmascript_client_reference_transition_name = app_project
2407 .as_ref()
2408 .map(|_| AppProject::client_transition_name());
2409
2410 if let Some(app_project) = app_project {
2411 transitions.push((
2412 AppProject::client_transition_name(),
2413 app_project
2414 .ecmascript_client_reference_transition()
2415 .to_resolved()
2416 .await?,
2417 ));
2418 }
2419
2420 Ok(Vc::upcast(ModuleAssetContext::new(
2421 TransitionOptions {
2422 named_transitions: transitions.into_iter().collect(),
2423 ..Default::default()
2424 }
2425 .cell(),
2426 self.server_compile_time_info(),
2427 get_server_module_options_context(
2428 self.project_path().owned().await?,
2429 self.execution_context(),
2430 ServerContextType::Instrumentation {
2431 app_dir: app_dir.clone(),
2432 ecmascript_client_reference_transition_name:
2433 ecmascript_client_reference_transition_name.clone(),
2434 },
2435 self.next_mode(),
2436 self.next_config(),
2437 NextRuntime::NodeJs,
2438 self.encryption_key(),
2439 self.server_compile_time_info().environment(),
2440 self.client_compile_time_info().environment(),
2441 *self.should_write_nft_manifests().await?,
2442 ),
2443 get_server_resolve_options_context(
2444 self.project_path().owned().await?,
2445 ServerContextType::Instrumentation {
2446 app_dir: app_dir.clone(),
2447 ecmascript_client_reference_transition_name,
2448 },
2449 self.next_mode(),
2450 self.next_config(),
2451 self.execution_context(),
2452 None, ),
2454 Layer::new_with_user_friendly_name(
2455 rcstr!("instrumentation"),
2456 rcstr!("Instrumentation"),
2457 ),
2458 )))
2459 }
2460
2461 #[turbo_tasks::function]
2462 async fn edge_instrumentation_context(self: Vc<Self>) -> Result<Vc<Box<dyn AssetContext>>> {
2463 let mut transitions = vec![];
2464
2465 let app_dir = find_app_dir(self.project_path().owned().await?)
2466 .owned()
2467 .await?;
2468 let app_project = &*self.app_project().await?;
2469
2470 let ecmascript_client_reference_transition_name = app_project
2471 .as_ref()
2472 .map(|_| AppProject::client_transition_name());
2473
2474 if let Some(app_project) = app_project {
2475 transitions.push((
2476 AppProject::client_transition_name(),
2477 app_project
2478 .edge_ecmascript_client_reference_transition()
2479 .to_resolved()
2480 .await?,
2481 ));
2482 }
2483
2484 Ok(Vc::upcast(ModuleAssetContext::new(
2485 TransitionOptions {
2486 named_transitions: transitions.into_iter().collect(),
2487 ..Default::default()
2488 }
2489 .cell(),
2490 self.edge_compile_time_info(),
2491 get_server_module_options_context(
2492 self.project_path().owned().await?,
2493 self.execution_context(),
2494 ServerContextType::Instrumentation {
2495 app_dir: app_dir.clone(),
2496 ecmascript_client_reference_transition_name:
2497 ecmascript_client_reference_transition_name.clone(),
2498 },
2499 self.next_mode(),
2500 self.next_config(),
2501 NextRuntime::Edge,
2502 self.encryption_key(),
2503 self.edge_compile_time_info().environment(),
2504 self.client_compile_time_info().environment(),
2505 false,
2507 ),
2508 get_edge_resolve_options_context(
2509 self.project_path().owned().await?,
2510 ServerContextType::Instrumentation {
2511 app_dir: app_dir.clone(),
2512 ecmascript_client_reference_transition_name,
2513 },
2514 self.next_mode(),
2515 self.next_config(),
2516 self.execution_context(),
2517 None, ),
2519 Layer::new_with_user_friendly_name(
2520 rcstr!("instrumentation-edge"),
2521 rcstr!("Edge Instrumentation"),
2522 ),
2523 )))
2524 }
2525
2526 #[turbo_tasks::function]
2527 async fn find_instrumentation(self: Vc<Self>) -> Result<Vc<FindContextFileResult>> {
2528 Ok(find_context_file(
2529 self.project_path().owned().await?,
2530 instrumentation_files(self.next_config().page_extensions()),
2531 false,
2533 ))
2534 }
2535
2536 #[turbo_tasks::function]
2537 async fn instrumentation_endpoint(
2538 self: Vc<Self>,
2539 is_edge: bool,
2540 ) -> Result<Vc<Box<dyn Endpoint>>> {
2541 let instrumentation = self.find_instrumentation();
2542 let FindContextFileResult::Found(fs_path, _) = &*instrumentation.await? else {
2543 return Ok(Vc::upcast(EmptyEndpoint::new(self)));
2544 };
2545 let source = Vc::upcast(FileSource::new(fs_path.clone()));
2546 let app_dir = find_app_dir(self.project_path().owned().await?)
2547 .owned()
2548 .await?;
2549 let ecmascript_client_reference_transition_name = (*self.app_project().await?)
2550 .as_ref()
2551 .map(|_| AppProject::client_transition_name());
2552
2553 let instrumentation_asset_context = if is_edge {
2554 self.edge_instrumentation_context()
2555 } else {
2556 self.node_instrumentation_context()
2557 };
2558
2559 Ok(Vc::upcast(InstrumentationEndpoint::new(
2560 self,
2561 instrumentation_asset_context,
2562 source,
2563 is_edge,
2564 app_dir.clone(),
2565 ecmascript_client_reference_transition_name,
2566 )))
2567 }
2568
2569 #[turbo_tasks::function]
2570 pub async fn emit_all_output_assets(
2571 self: Vc<Self>,
2572 output_assets: OperationVc<OutputAssets>,
2573 ) -> Result<()> {
2574 let span = tracing::info_span!("emitting");
2575 async move {
2576 let all_output_assets = all_assets_from_entries_operation(output_assets);
2577
2578 let client_relative_path = self.client_relative_path().owned().await?;
2579 let node_root = self.node_root().owned().await?;
2580
2581 if let Some(map) = self.await?.versioned_content_map {
2582 map.insert_output_assets(
2583 all_output_assets,
2584 node_root.clone(),
2585 client_relative_path.clone(),
2586 node_root.clone(),
2587 )
2588 .as_side_effect()
2589 .await?;
2590
2591 Ok(())
2592 } else {
2593 emit_assets(
2594 all_output_assets.connect(),
2595 node_root.clone(),
2596 client_relative_path.clone(),
2597 node_root.clone(),
2598 )
2599 .as_side_effect()
2600 .await?;
2601
2602 Ok(())
2603 }
2604 }
2605 .instrument(span)
2606 .await
2607 }
2608
2609 #[turbo_tasks::function]
2610 async fn server_hmr_root_path(self: Vc<Self>) -> Result<Vc<FileSystemPath>> {
2611 Ok(self.node_root().await?.join("server/app")?.cell())
2612 }
2613
2614 #[turbo_tasks::function]
2616 async fn hmr_content(self: Vc<Self>, chunk_name: RcStr) -> Result<Vc<OptionVersionedContent>> {
2617 if let Some(map) = self.await?.versioned_content_map {
2618 let content = map.get(self.client_relative_path().await?.join(&chunk_name)?);
2619 Ok(content)
2620 } else {
2621 bail!("must be in dev mode to hmr")
2622 }
2623 }
2624
2625 #[turbo_tasks::function]
2628 pub async fn hmr_version_state(
2629 self: ResolvedVc<Self>,
2630 chunk_name: RcStr,
2631 session: TransientInstance<()>,
2632 ) -> Result<Vc<VersionState>> {
2633 let _ = session;
2636
2637 #[tracing::instrument(
2638 level = "info",
2639 name = "get HMR version",
2640 skip_all,
2641 fields(chunk_name = %chunk_name),
2642 )]
2643 #[turbo_tasks::function(operation, root)]
2644 async fn hmr_version_operation(
2645 this: ResolvedVc<Project>,
2646 chunk_name: RcStr,
2647 ) -> Result<Vc<Box<dyn Version>>> {
2648 tracing::info!(chunk_name = %chunk_name, "hmr subscription");
2649 let content = this.hmr_content(chunk_name).await?;
2650 if let Some(content) = &*content {
2651 Ok(content.version())
2652 } else {
2653 Ok(Vc::upcast(NotFoundVersion::new()))
2654 }
2655 }
2656 let version_op = hmr_version_operation(self, chunk_name);
2657
2658 let state = VersionState::new(
2662 version_op
2663 .read_trait_strongly_consistent()
2664 .untracked()
2665 .await?,
2666 )
2667 .await?;
2668 Ok(state)
2669 }
2670
2671 #[turbo_tasks::function]
2674 pub async fn hmr_update(
2675 self: Vc<Self>,
2676 chunk_name: RcStr,
2677 from: Vc<VersionState>,
2678 ) -> Result<Vc<Update>> {
2679 let from = from.get();
2680 let content = self.hmr_content(chunk_name).await?;
2681 if let Some(content) = *content {
2682 Ok(content.update(from))
2683 } else {
2684 Ok(Update::Missing.cell())
2685 }
2686 }
2687
2688 #[turbo_tasks::function]
2690 pub async fn server_hmr_chunks(self: Vc<Self>) -> Result<Vc<ServerHmrChunkLists>> {
2691 let Some(map) = self.await?.versioned_content_map else {
2692 bail!("must be in dev mode to hmr")
2693 };
2694 let root = self.server_hmr_root_path().owned().await?;
2695 Ok(map.server_hmr_chunks_in_path(root))
2696 }
2697
2698 #[turbo_tasks::function]
2699 pub async fn server_hmr_chunks_for_entries(
2700 self: Vc<Self>,
2701 entry_paths: Vec<RcStr>,
2702 ) -> Result<Vc<ServerHmrChunkLists>> {
2703 let mut chunk_lists =
2704 ServerHmrChunkLists::new(self.server_hmr_chunks().await?.as_slice().to_vec());
2705 chunk_lists.retain_entry_paths(&entry_paths.into_iter().collect());
2706 Ok(chunk_lists.cell())
2707 }
2708
2709 #[turbo_tasks::function]
2711 pub async fn hmr_chunk_names(self: Vc<Self>) -> Result<Vc<Vec<RcStr>>> {
2712 if let Some(map) = self.await?.versioned_content_map {
2713 Ok(map.keys_in_path(self.client_relative_path().owned().await?))
2714 } else {
2715 bail!("must be in dev mode to hmr")
2716 }
2717 }
2718
2719 #[turbo_tasks::function]
2722 pub async fn server_changed(self: Vc<Self>, roots: Vc<OutputAssets>) -> Result<Vc<Completion>> {
2723 let path = self.node_root().owned().await?;
2724 Ok(any_output_changed(roots, path, true))
2725 }
2726
2727 #[turbo_tasks::function]
2730 pub async fn client_changed(self: Vc<Self>, roots: Vc<OutputAssets>) -> Result<Vc<Completion>> {
2731 let path = self.client_root().owned().await?;
2732 Ok(any_output_changed(roots, path, false))
2733 }
2734
2735 #[turbo_tasks::function]
2736 pub async fn client_main_modules(self: Vc<Self>) -> Result<Vc<GraphEntries>> {
2737 let pages_project = self.pages_project();
2738 let mut chunk_groups = vec![ChunkGroupEntry::Entry {
2739 modules: vec![pages_project.client_main_module().to_resolved().await?],
2740 heuristics: EntryHeuristics::high_priority(),
2741 }];
2742
2743 if let Some(app_project) = *self.app_project().await? {
2744 chunk_groups.push(ChunkGroupEntry::Entry {
2745 modules: vec![app_project.client_main_module().to_resolved().await?],
2746 heuristics: EntryHeuristics::high_priority(),
2747 });
2748 }
2749
2750 Ok(GraphEntries::from_chunk_groups(chunk_groups).cell())
2751 }
2752
2753 #[turbo_tasks::function]
2755 pub async fn module_ids(self: Vc<Self>) -> Result<Vc<ModuleIdStrategy>> {
2756 let module_id_strategy = *self.next_config().module_ids(self.next_mode()).await?;
2757 match module_id_strategy {
2758 ModuleIdStrategyConfig::Named => Ok(ModuleIdStrategy {
2759 module_id_map: None,
2760 fallback: ModuleIdFallback::Ident,
2761 }
2762 .cell()),
2763 ModuleIdStrategyConfig::Deterministic => {
2764 let module_graphs = self.whole_app_module_graphs().await?;
2765 Ok(get_global_module_id_strategy(*module_graphs.full))
2766 }
2767 }
2768 }
2769
2770 #[turbo_tasks::function]
2772 async fn binding_usage_info(self: Vc<Self>) -> Result<Vc<BindingUsageInfo>> {
2773 let module_graphs = self.whole_app_module_graphs().await?;
2774 Ok(module_graphs
2775 .binding_usage_info
2776 .context("No binding usage info")?
2777 .connect())
2778 }
2779
2780 #[turbo_tasks::function]
2782 pub async fn export_usage(self: Vc<Self>) -> Result<Vc<OptionBindingUsageInfo>> {
2783 if *self
2784 .next_config()
2785 .turbopack_remove_unused_exports(self.next_mode())
2786 .await?
2787 {
2788 Ok(Vc::cell(Some(
2789 self.binding_usage_info().to_resolved().await?,
2790 )))
2791 } else {
2792 Ok(Vc::cell(None))
2793 }
2794 }
2795
2796 #[turbo_tasks::function]
2798 pub async fn unused_references(self: Vc<Self>) -> Result<Vc<UnusedReferences>> {
2799 if *self
2800 .next_config()
2801 .turbopack_remove_unused_imports(self.next_mode())
2802 .await?
2803 {
2804 Ok(self.binding_usage_info().unused_references())
2805 } else {
2806 Ok(Vc::cell(Default::default()))
2807 }
2808 }
2809
2810 #[turbo_tasks::function]
2811 pub async fn with_next_config(&self, next_config: Vc<NextConfig>) -> Result<Vc<Self>> {
2812 Ok(Self {
2813 next_config: next_config.to_resolved().await?,
2814 ..(*self).clone()
2815 }
2816 .cell())
2817 }
2818
2819 #[turbo_tasks::function]
2821 pub async fn additional_traced_modules(self: Vc<Self>) -> Result<Vc<Modules>> {
2822 let project_path = self.project_path().owned().await?;
2823 let cache_handler = self
2824 .next_config()
2825 .cache_handler(project_path.clone())
2826 .await?;
2827 let cache_handlers = self
2828 .next_config()
2829 .cache_handlers(project_path.clone())
2830 .await?;
2831
2832 let asset_context = externals_tracing_module_context(
2833 get_tracing_compile_time_info(),
2834 false,
2835 None,
2836 true,
2837 );
2838
2839 Ok(Vc::cell(
2840 cache_handler
2841 .iter()
2842 .chain(cache_handlers.iter())
2843 .map(|f| {
2844 asset_context
2845 .process(
2846 Vc::upcast(FileSource::new(f.clone())),
2847 ReferenceType::CommonJs(CommonJsReferenceSubType::Undefined),
2848 )
2849 .module()
2850 })
2851 .map(|m| m.to_resolved())
2852 .try_join()
2853 .await?,
2854 ))
2855 }
2856
2857 #[turbo_tasks::function]
2861 pub async fn pages_traced_modules(self: Vc<Self>) -> Result<Vc<Modules>> {
2862 let asset_context = Vc::upcast(externals_tracing_module_context(
2863 get_tracing_compile_time_info(),
2864 false,
2865 None,
2866 true,
2867 ));
2868 let hook_modules = require_hook_modules(self.project_path().owned().await?, asset_context)
2869 .owned()
2870 .await?;
2871 let renderer_modules =
2872 pages_renderer_modules(self.project_path().owned().await?, asset_context)
2873 .owned()
2874 .await?;
2875
2876 Ok(Vc::cell(
2877 self.additional_traced_modules()
2878 .owned()
2879 .await?
2880 .into_iter()
2881 .chain(hook_modules)
2882 .chain(renderer_modules)
2883 .collect(),
2884 ))
2885 }
2886}
2887
2888async fn scale_down_node_pool(project: ResolvedVc<Project>) -> Result<()> {
2890 let execution_context = project.execution_context().await?;
2891 let node_backend = execution_context.node_backend.into_trait_ref().await?;
2892 if *project.is_watch_enabled().await? {
2893 node_backend.scale_down()?;
2894 } else {
2895 node_backend.scale_zero()?;
2896 }
2897 Ok(())
2898}
2899
2900#[turbo_tasks::function(operation, root)]
2903async fn whole_app_module_graph_operation(
2904 project: ResolvedVc<Project>,
2905) -> Result<Vc<BaseAndFullModuleGraph>> {
2906 let start = Instant::now();
2907 let wall_start = SystemTime::now();
2908 let span = tracing::info_span!("whole app module graph", modules = Empty, edges = Empty);
2909 let span_clone = span.clone();
2910 let result = async move {
2911 let next_mode = project.next_mode();
2912 let should_trace = *project.should_write_nft_manifests().await?;
2913 let should_read_binding_usage = next_mode.await?.is_production();
2914 let base_single_module_graph = SingleModuleGraph::new_with_entries(
2915 project.get_all_entries().to_resolved().await?,
2916 should_trace,
2917 should_read_binding_usage,
2918 );
2919 let base_visited_modules = VisitedModules::from_graph(base_single_module_graph);
2920
2921 let base = ModuleGraph::from_graphs(vec![base_single_module_graph], None);
2922
2923 let turbopack_remove_unused_imports = *project
2924 .next_config()
2925 .turbopack_remove_unused_imports(next_mode)
2926 .await?;
2927
2928 let base = if turbopack_remove_unused_imports {
2929 let binding_usage_info = compute_binding_usage_info(base, true);
2932 ModuleGraph::from_graphs(vec![base_single_module_graph], Some(binding_usage_info))
2933 } else {
2934 base
2935 };
2936
2937 let additional_entries = project
2938 .get_all_additional_entries(base.connect())
2939 .to_resolved()
2940 .await?;
2941
2942 let additional_module_graph = SingleModuleGraph::new_with_entries_visited(
2943 additional_entries,
2944 base_visited_modules,
2945 should_trace,
2946 should_read_binding_usage,
2947 );
2948
2949 if !span.is_disabled() {
2950 let base_module_count = base_single_module_graph
2951 .connect()
2952 .module_count()
2953 .untracked()
2954 .await?;
2955 let additional_module_count = additional_module_graph
2956 .connect()
2957 .module_count()
2958 .untracked()
2959 .await?;
2960 span.record("modules", *base_module_count + *additional_module_count);
2961 let base_edge_count = base_single_module_graph
2962 .connect()
2963 .edge_count()
2964 .untracked()
2965 .await?;
2966 let additional_edge_count = additional_module_graph
2967 .connect()
2968 .edge_count()
2969 .untracked()
2970 .await?;
2971 span.record("edges", *base_edge_count + *additional_edge_count);
2972 }
2973
2974 let graphs = vec![base_single_module_graph, additional_module_graph];
2975
2976 let (full, binding_usage_info) = if turbopack_remove_unused_imports {
2977 let full_with_unused_references = ModuleGraph::from_graphs(graphs.clone(), None);
2978 let binding_usage_info = compute_binding_usage_info(full_with_unused_references, true);
2979 (
2980 ModuleGraph::from_graphs(graphs, Some(binding_usage_info)),
2981 Some(binding_usage_info),
2982 )
2983 } else {
2984 (ModuleGraph::from_graphs(graphs, None), None)
2985 };
2986
2987 Ok(BaseAndFullModuleGraph {
2988 base: base.connect().to_resolved().await?,
2989 full: full.connect().to_resolved().await?,
2990 binding_usage_info,
2991 }
2992 .cell())
2993 }
2994 .instrument(span_clone)
2995 .await;
2996 turbo_tasks().send_compilation_event(Arc::new(TraceEvent::new_with_duration(
2997 "turbopack-module-graph",
2998 wall_start,
2999 start.elapsed(),
3000 serde_json::json!([]),
3001 )));
3002 result
3003}
3004
3005#[turbo_tasks::value(shared)]
3006pub struct BaseAndFullModuleGraph {
3007 pub base: ResolvedVc<ModuleGraph>,
3009 pub full: ResolvedVc<ModuleGraph>,
3011 pub binding_usage_info: Option<OperationVc<BindingUsageInfo>>,
3013}
3014
3015#[turbo_tasks::function]
3016async fn any_output_changed(
3017 roots: Vc<OutputAssets>,
3018 path: FileSystemPath,
3019 server: bool,
3020) -> Result<Vc<Completion>> {
3021 let all_assets = expand_output_assets(
3022 roots.await?.into_iter().map(ExpandOutputAssetsInput::Asset),
3023 true,
3024 )
3025 .await?;
3026 let completions = all_assets
3027 .into_iter()
3028 .map(|m| {
3029 let path = path.clone();
3030
3031 async move {
3032 let asset_path = m.path().await?;
3033 if !asset_path.path.ends_with(".map")
3034 && (!server || !asset_path.path.ends_with(".css"))
3035 && asset_path.is_inside_ref(&path)
3036 {
3037 anyhow::Ok(Some(
3038 content_changed(*ResolvedVc::upcast(m))
3039 .to_resolved()
3040 .await?,
3041 ))
3042 } else {
3043 Ok(None)
3044 }
3045 }
3046 })
3047 .try_flat_join()
3048 .await?;
3049
3050 Ok(Vc::<Completions>::cell(completions).completed())
3051}
3052
3053#[turbo_tasks::function(operation, root)]
3054fn all_assets_from_entries_operation(
3055 operation: OperationVc<OutputAssets>,
3056) -> Result<Vc<ExpandedOutputAssets>> {
3057 let assets = operation.connect();
3058 Ok(all_assets_from_entries(assets))
3059}