next_api/
project.rs

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