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        parse::Request,
16    },
17};
18
19pub async fn handle_resolve_error(
20    result: Vc<ModuleResolveResult>,
21    reference_type: ReferenceType,
22    origin_path: FileSystemPath,
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() {
32                emit_unresolvable_issue(
33                    error_mode,
34                    &origin_path,
35                    reference_type,
36                    request,
37                    resolve_options,
38                    source,
39                )
40                .await?;
41            }
42
43            handle_item_issues(result_ref.errors(), &origin_path, source).await?;
44
45            result
46        }
47        Err(err) => {
48            emit_resolve_error_issue(
49                error_mode,
50                &origin_path,
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_path: FileSystemPath,
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() {
75                emit_unresolvable_issue(
76                    error_mode,
77                    &origin_path,
78                    reference_type,
79                    request,
80                    resolve_options,
81                    source,
82                )
83                .await?;
84            }
85
86            handle_item_issues(result_ref.errors(), &origin_path, source).await?;
87
88            result
89        }
90        Err(err) => {
91            emit_resolve_error_issue(
92                error_mode,
93                &origin_path,
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_path: &FileSystemPath,
109    source: Option<IssueSource>,
110) -> Result<()> {
111    let mut items = items.peekable();
112    if items.peek().is_some() {
113        for item in items {
114            let trait_ref = item.into_trait_ref().await?;
115            ResolvingIssueWithLocation {
116                inner: item,
117                severity: trait_ref.severity(),
118                stage: trait_ref.stage(),
119                documentation_link: trait_ref.documentation_link(),
120                file_path: origin_path.clone(),
121                source,
122            }
123            .resolved_cell()
124            .emit();
125        }
126    }
127    Ok(())
128}
129
130async fn emit_resolve_error_issue(
131    error_mode: ResolveErrorMode,
132    origin_path: &FileSystemPath,
133    reference_type: ReferenceType,
134    request: Vc<Request>,
135    resolve_options: Vc<ResolveOptions>,
136    err: anyhow::Error,
137    source: Option<IssueSource>,
138) -> Result<()> {
139    if error_mode == ResolveErrorMode::Ignore {
140        return Ok(());
141    }
142    let severity = if error_mode == ResolveErrorMode::Warn || resolve_options.await?.loose_errors {
143        IssueSeverity::Warning
144    } else {
145        IssueSeverity::Error
146    };
147    ResolvingIssue {
148        severity,
149        file_path: origin_path.clone(),
150        request_type: format!("{reference_type} request"),
151        request: request.to_resolved().await?,
152        resolve_options: resolve_options.to_resolved().await?,
153        error_message: Some(format!("{}", PrettyPrintError(&err))),
154        source,
155    }
156    .resolved_cell()
157    .emit();
158    Ok(())
159}
160
161async fn emit_unresolvable_issue(
162    error_mode: ResolveErrorMode,
163    origin_path: &FileSystemPath,
164    reference_type: ReferenceType,
165    request: Vc<Request>,
166    resolve_options: Vc<ResolveOptions>,
167    source: Option<IssueSource>,
168) -> Result<()> {
169    if error_mode == ResolveErrorMode::Ignore {
170        return Ok(());
171    }
172    let severity = if error_mode == ResolveErrorMode::Warn || resolve_options.await?.loose_errors {
173        IssueSeverity::Warning
174    } else {
175        IssueSeverity::Error
176    };
177    ResolvingIssue {
178        severity,
179        file_path: origin_path.clone(),
180        request_type: format!("{reference_type} request"),
181        request: request.to_resolved().await?,
182        resolve_options: resolve_options.to_resolved().await?,
183        error_message: None,
184        source,
185    }
186    .resolved_cell()
187    .emit();
188    Ok(())
189}
190
191pub async fn resolve_error_severity(resolve_options: Vc<ResolveOptions>) -> Result<IssueSeverity> {
192    Ok(if resolve_options.await?.loose_errors {
193        IssueSeverity::Warning
194    } else {
195        IssueSeverity::Error
196    })
197}
198
199/// Delegates to the inner issue but overrides the file path and source information.
200#[turbo_tasks::value(shared)]
201pub struct ResolvingIssueWithLocation {
202    pub inner: ResolvedVc<Box<dyn Issue>>,
203    pub severity: IssueSeverity,
204    pub stage: IssueStage,
205    pub documentation_link: RcStr,
206    pub file_path: FileSystemPath,
207    pub source: Option<IssueSource>,
208}
209
210#[async_trait]
211#[turbo_tasks::value_impl]
212impl Issue for ResolvingIssueWithLocation {
213    fn severity(&self) -> IssueSeverity {
214        self.severity
215    }
216
217    async fn file_path(&self) -> Result<FileSystemPath> {
218        Ok(self.file_path.clone())
219    }
220
221    fn stage(&self) -> IssueStage {
222        self.stage.clone()
223    }
224
225    async fn title(&self) -> Result<StyledString> {
226        self.inner.into_trait_ref().await?.title().await
227    }
228
229    async fn description(&self) -> Result<Option<StyledString>> {
230        self.inner.into_trait_ref().await?.description().await
231    }
232
233    async fn detail(&self) -> Result<Option<StyledString>> {
234        self.inner.into_trait_ref().await?.detail().await
235    }
236
237    fn documentation_link(&self) -> RcStr {
238        self.documentation_link.clone()
239    }
240
241    fn source(&self) -> Option<IssueSource> {
242        self.source
243    }
244}