Skip to main content

turbopack_core/resolve/
error.rs

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