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#[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}