Skip to main content

turbopack_browser/
react_refresh.rs

1use anyhow::Result;
2use async_trait::async_trait;
3use turbo_rcstr::rcstr;
4use turbo_tasks::{ResolvedVc, Vc};
5use turbo_tasks_fs::FileSystemPath;
6use turbopack_core::{
7    issue::{Issue, IssueExt, IssueSeverity, IssueStage, StyledString},
8    reference_type::{CommonJsReferenceSubType, ReferenceType},
9    resolve::parse::Request,
10};
11use turbopack_resolve::{
12    ecmascript::apply_cjs_specific_options, resolve_options_context::ResolveOptionsContext,
13};
14
15#[turbo_tasks::function]
16fn react_refresh_request() -> Vc<Request> {
17    Request::parse_string(rcstr!("@next/react-refresh-utils/dist/runtime"))
18}
19
20#[turbo_tasks::function]
21fn react_refresh_request_in_next() -> Vc<Request> {
22    Request::parse_string(rcstr!(
23        "next/dist/compiled/@next/react-refresh-utils/dist/runtime"
24    ))
25}
26
27#[turbo_tasks::value]
28pub enum ResolveReactRefreshResult {
29    NotFound,
30    Found(ResolvedVc<Request>),
31}
32
33impl ResolveReactRefreshResult {
34    pub fn as_request(&self) -> Option<Vc<Request>> {
35        match self {
36            ResolveReactRefreshResult::NotFound => None,
37            ResolveReactRefreshResult::Found(r) => Some(**r),
38        }
39    }
40    pub fn is_found(&self) -> bool {
41        match self {
42            ResolveReactRefreshResult::NotFound => false,
43            ResolveReactRefreshResult::Found(_) => true,
44        }
45    }
46}
47
48/// Checks whether we can resolve the React Refresh runtime module from the
49/// given path. Emits an issue if we can't.
50#[turbo_tasks::function]
51pub async fn assert_can_resolve_react_refresh(
52    path: FileSystemPath,
53    resolve_options_context: Vc<ResolveOptionsContext>,
54) -> Result<Vc<ResolveReactRefreshResult>> {
55    let resolve_options = apply_cjs_specific_options(turbopack_resolve::resolve::resolve_options(
56        path.clone(),
57        resolve_options_context,
58    ));
59    for request in [react_refresh_request_in_next(), react_refresh_request()] {
60        let result = turbopack_core::resolve::resolve(
61            path.clone(),
62            ReferenceType::CommonJs(CommonJsReferenceSubType::Undefined),
63            request,
64            resolve_options,
65        )
66        .first_source();
67
68        if result.await?.is_some() {
69            return Ok(ResolveReactRefreshResult::Found(request.to_resolved().await?).cell());
70        }
71    }
72    ReactRefreshResolvingIssue { path }.resolved_cell().emit();
73    Ok(ResolveReactRefreshResult::NotFound.cell())
74}
75
76/// An issue that occurred while resolving the React Refresh runtime module.
77#[turbo_tasks::value(shared)]
78pub struct ReactRefreshResolvingIssue {
79    path: FileSystemPath,
80}
81
82#[async_trait]
83#[turbo_tasks::value_impl]
84impl Issue for ReactRefreshResolvingIssue {
85    fn severity(&self) -> IssueSeverity {
86        IssueSeverity::Warning
87    }
88
89    async fn title(&self) -> Result<StyledString> {
90        Ok(StyledString::Text(rcstr!(
91            "Could not resolve React Refresh runtime"
92        )))
93    }
94
95    fn stage(&self) -> IssueStage {
96        IssueStage::Resolve
97    }
98
99    async fn file_path(&self) -> Result<FileSystemPath> {
100        Ok(self.path.clone())
101    }
102
103    async fn description(&self) -> Result<Option<StyledString>> {
104        Ok(Some(StyledString::Line(vec![
105            StyledString::Text(rcstr!(
106                "React Refresh will be disabled.\nTo enable React Refresh, install the "
107            )),
108            StyledString::Code(rcstr!("react-refresh")),
109            StyledString::Text(rcstr!(" and ")),
110            StyledString::Code(rcstr!("@next/react-refresh-utils")),
111            StyledString::Text(rcstr!(" modules.")),
112        ])))
113    }
114}