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