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