Skip to main content

turbopack_core/resolve/
error.rs

1use anyhow::Result;
2use turbo_rcstr::RcStr;
3use turbo_tasks::{IntoTraitRef, PrettyPrintError, ResolvedVc, Vc};
4use turbo_tasks_fs::FileSystemPath;
5
6use crate::{
7    issue::{
8        Issue, IssueExt, IssueSeverity, IssueSource, IssueStage, OptionIssueSource,
9        OptionStyledString, StyledString, resolve::ResolvingIssue,
10    },
11    reference_type::ReferenceType,
12    resolve::{
13        ModuleResolveResult, ResolveErrorMode, ResolveResult, options::ResolveOptions,
14        origin::ResolveOrigin, parse::Request,
15    },
16};
17
18pub async fn handle_resolve_error(
19    result: Vc<ModuleResolveResult>,
20    reference_type: ReferenceType,
21    origin: Vc<Box<dyn ResolveOrigin>>,
22    request: Vc<Request>,
23    resolve_options: Vc<ResolveOptions>,
24    error_mode: ResolveErrorMode,
25
26    source: Option<IssueSource>,
27) -> Result<Vc<ModuleResolveResult>> {
28    Ok(match result.await {
29        Ok(result_ref) => {
30            if result_ref.is_unresolvable_ref() {
31                emit_unresolvable_issue(
32                    error_mode,
33                    origin,
34                    reference_type,
35                    request,
36                    resolve_options,
37                    source,
38                )
39                .await?;
40            }
41
42            handle_item_issues(result_ref.errors(), origin, source).await?;
43
44            result
45        }
46        Err(err) => {
47            emit_resolve_error_issue(
48                error_mode,
49                origin,
50                reference_type,
51                request,
52                resolve_options,
53                err,
54                source,
55            )
56            .await?;
57            *ModuleResolveResult::unresolvable()
58        }
59    })
60}
61
62pub async fn handle_resolve_source_error(
63    result: Vc<ResolveResult>,
64    reference_type: ReferenceType,
65    origin: Vc<Box<dyn ResolveOrigin>>,
66    request: Vc<Request>,
67    resolve_options: Vc<ResolveOptions>,
68    error_mode: ResolveErrorMode,
69    source: Option<IssueSource>,
70) -> Result<Vc<ResolveResult>> {
71    Ok(match result.await {
72        Ok(result_ref) => {
73            if result_ref.is_unresolvable_ref() {
74                emit_unresolvable_issue(
75                    error_mode,
76                    origin,
77                    reference_type,
78                    request,
79                    resolve_options,
80                    source,
81                )
82                .await?;
83            }
84
85            handle_item_issues(result_ref.errors(), origin, source).await?;
86
87            result
88        }
89        Err(err) => {
90            emit_resolve_error_issue(
91                error_mode,
92                origin,
93                reference_type,
94                request,
95                resolve_options,
96                err,
97                source,
98            )
99            .await?;
100            ResolveResult::unresolvable().cell()
101        }
102    })
103}
104
105async fn handle_item_issues(
106    items: impl Iterator<Item = ResolvedVc<Box<dyn Issue>>>,
107    origin: Vc<Box<dyn ResolveOrigin>>,
108    source: Option<IssueSource>,
109) -> Result<()> {
110    let mut items = items.peekable();
111    if items.peek().is_some() {
112        let file_path = origin.origin_path().owned().await?;
113        for item in items {
114            ResolvingIssueWithLocation {
115                inner: item,
116                severity: item.into_trait_ref().await?.severity(),
117                file_path: file_path.clone(),
118                source,
119            }
120            .resolved_cell()
121            .emit();
122        }
123    }
124    Ok(())
125}
126
127async fn emit_resolve_error_issue(
128    error_mode: ResolveErrorMode,
129    origin: Vc<Box<dyn ResolveOrigin>>,
130    reference_type: ReferenceType,
131    request: Vc<Request>,
132    resolve_options: Vc<ResolveOptions>,
133    err: anyhow::Error,
134    source: Option<IssueSource>,
135) -> Result<()> {
136    if error_mode == ResolveErrorMode::Ignore {
137        return Ok(());
138    }
139    let severity = if error_mode == ResolveErrorMode::Warn || resolve_options.await?.loose_errors {
140        IssueSeverity::Warning
141    } else {
142        IssueSeverity::Error
143    };
144    ResolvingIssue {
145        severity,
146        file_path: origin.origin_path().owned().await?,
147        request_type: format!("{reference_type} request"),
148        request: request.to_resolved().await?,
149        resolve_options: resolve_options.to_resolved().await?,
150        error_message: Some(format!("{}", PrettyPrintError(&err))),
151        source,
152    }
153    .resolved_cell()
154    .emit();
155    Ok(())
156}
157
158async fn emit_unresolvable_issue(
159    error_mode: ResolveErrorMode,
160
161    origin: Vc<Box<dyn ResolveOrigin>>,
162    reference_type: ReferenceType,
163    request: Vc<Request>,
164    resolve_options: Vc<ResolveOptions>,
165    source: Option<IssueSource>,
166) -> Result<()> {
167    if error_mode == ResolveErrorMode::Ignore {
168        return Ok(());
169    }
170    let severity = if error_mode == ResolveErrorMode::Warn || resolve_options.await?.loose_errors {
171        IssueSeverity::Warning
172    } else {
173        IssueSeverity::Error
174    };
175    ResolvingIssue {
176        severity,
177        file_path: origin.origin_path().owned().await?,
178        request_type: format!("{reference_type} request"),
179        request: request.to_resolved().await?,
180        resolve_options: resolve_options.to_resolved().await?,
181        error_message: None,
182        source,
183    }
184    .resolved_cell()
185    .emit();
186    Ok(())
187}
188
189pub async fn resolve_error_severity(resolve_options: Vc<ResolveOptions>) -> Result<IssueSeverity> {
190    Ok(if resolve_options.await?.loose_errors {
191        IssueSeverity::Warning
192    } else {
193        IssueSeverity::Error
194    })
195}
196
197/// Delegates to the inner issue but overrides the file path and source information.
198#[turbo_tasks::value(shared)]
199pub struct ResolvingIssueWithLocation {
200    pub inner: ResolvedVc<Box<dyn Issue>>,
201    pub severity: IssueSeverity,
202    pub file_path: FileSystemPath,
203    pub source: Option<IssueSource>,
204}
205
206#[turbo_tasks::value_impl]
207impl Issue for ResolvingIssueWithLocation {
208    fn severity(&self) -> IssueSeverity {
209        self.severity
210    }
211
212    #[turbo_tasks::function]
213    fn file_path(&self) -> Vc<FileSystemPath> {
214        self.file_path.clone().cell()
215    }
216
217    #[turbo_tasks::function]
218    fn stage(&self) -> Vc<IssueStage> {
219        self.inner.stage()
220    }
221
222    #[turbo_tasks::function]
223    fn title(&self) -> Vc<StyledString> {
224        self.inner.title()
225    }
226
227    #[turbo_tasks::function]
228    fn description(&self) -> Vc<OptionStyledString> {
229        self.inner.description()
230    }
231
232    #[turbo_tasks::function]
233    fn detail(&self) -> Vc<OptionStyledString> {
234        self.inner.detail()
235    }
236
237    #[turbo_tasks::function]
238    fn documentation_link(&self) -> Vc<RcStr> {
239        self.inner.documentation_link()
240    }
241
242    #[turbo_tasks::function]
243    fn source(&self) -> Vc<OptionIssueSource> {
244        Vc::cell(self.source)
245    }
246}