next_swc_napi/next_api/
turbopack_ctx.rs

1//! Utilities for constructing and using the [`NextTurbopackContext`] type.
2
3use std::{path::PathBuf, sync::Arc};
4
5use anyhow::Result;
6use either::Either;
7use serde::Serialize;
8use turbo_tasks::{
9    TurboTasks, TurboTasksApi,
10    message_queue::{CompilationEvent, Severity},
11};
12use turbo_tasks_backend::{
13    BackendOptions, DefaultBackingStorage, GitVersionInfo, NoopBackingStorage, StartupCacheState,
14    TurboTasksBackend, db_invalidation::invalidation_reasons, default_backing_storage,
15    noop_backing_storage,
16};
17
18pub type NextTurboTasks =
19    Arc<TurboTasks<TurboTasksBackend<Either<DefaultBackingStorage, NoopBackingStorage>>>>;
20
21/// A value often wrapped in [`napi::bindgen_prelude::External`] that retains the Turbopack instance
22/// used by Next.js, and various napi helpers that may have been passed to us from JS.
23///
24/// This is not a [`turbo_tasks::value`], and should only be used within the top-level napi layer.
25/// It should not be passed to a [`turbo_tasks::function`]. For serializable information about the
26/// project, use the [`next_api::project::Project`] type.
27#[derive(Clone)]
28pub struct NextTurbopackContext {
29    inner: Arc<NextTurboContextInner>,
30}
31
32struct NextTurboContextInner {
33    turbo_tasks: NextTurboTasks,
34}
35
36impl NextTurbopackContext {
37    pub fn new(turbo_tasks: NextTurboTasks) -> Self {
38        NextTurbopackContext {
39            inner: Arc::new(NextTurboContextInner { turbo_tasks }),
40        }
41    }
42
43    pub fn turbo_tasks(&self) -> &NextTurboTasks {
44        &self.inner.turbo_tasks
45    }
46}
47
48pub fn create_turbo_tasks(
49    output_path: PathBuf,
50    persistent_caching: bool,
51    _memory_limit: usize,
52    dependency_tracking: bool,
53    is_ci: bool,
54) -> Result<NextTurboTasks> {
55    Ok(if persistent_caching {
56        let version_info = GitVersionInfo {
57            describe: env!("VERGEN_GIT_DESCRIBE"),
58            dirty: option_env!("CI").is_none_or(|value| value.is_empty())
59                && env!("VERGEN_GIT_DIRTY") == "true",
60        };
61        let (backing_storage, cache_state) =
62            default_backing_storage(&output_path.join("cache/turbopack"), &version_info, is_ci)?;
63        let tt = TurboTasks::new(TurboTasksBackend::new(
64            BackendOptions {
65                storage_mode: Some(if std::env::var("TURBO_ENGINE_READ_ONLY").is_ok() {
66                    turbo_tasks_backend::StorageMode::ReadOnly
67                } else {
68                    turbo_tasks_backend::StorageMode::ReadWrite
69                }),
70                dependency_tracking,
71                ..Default::default()
72            },
73            Either::Left(backing_storage),
74        ));
75        if let StartupCacheState::Invalidated { reason_code } = cache_state {
76            tt.send_compilation_event(Arc::new(StartupCacheInvalidationEvent { reason_code }));
77        }
78        tt
79    } else {
80        TurboTasks::new(TurboTasksBackend::new(
81            BackendOptions {
82                storage_mode: None,
83                dependency_tracking,
84                ..Default::default()
85            },
86            Either::Right(noop_backing_storage()),
87        ))
88    })
89}
90
91#[derive(Serialize)]
92struct StartupCacheInvalidationEvent {
93    reason_code: Option<String>,
94}
95
96impl CompilationEvent for StartupCacheInvalidationEvent {
97    fn type_name(&self) -> &'static str {
98        "StartupCacheInvalidationEvent"
99    }
100
101    fn severity(&self) -> Severity {
102        Severity::Warning
103    }
104
105    fn message(&self) -> String {
106        let reason_msg = match self.reason_code.as_deref() {
107            Some(invalidation_reasons::PANIC) => {
108                " because we previously detected an internal error in Turbopack"
109            }
110            Some(invalidation_reasons::USER_REQUEST) => " as the result of a user request",
111            _ => "", // ignore unknown reasons
112        };
113        format!(
114            "Turbopack's persistent cache has been deleted{reason_msg}. Builds or page loads may \
115             be slower as a result."
116        )
117    }
118
119    fn to_json(&self) -> String {
120        serde_json::to_string(self).unwrap()
121    }
122}