turbopack_browser/
react_refresh.rs

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