1use std::{fmt::Display, sync::Arc};
2
3use miette::{Diagnostic, LabeledSpan, MietteDiagnostic, Severity, SourceCode, SourceSpan};
4use swc_core::common::SourceFile;
5use thiserror::Error;
6
7use crate::RspackSeverity;
8
9#[derive(Debug, Error)]
10#[error(transparent)]
11pub struct InternalError(#[from] Box<dyn Diagnostic + Send + Sync + 'static>);
12
13impl InternalError {
14 pub fn new(message: String, severity: RspackSeverity) -> Self {
15 Self(Box::new(
16 MietteDiagnostic::new(message).with_severity(severity.into()),
17 ))
18 }
19}
20
21#[derive(Debug, Error)]
24#[error(transparent)]
25pub struct DiagnosticError(Box<dyn std::error::Error + Send + Sync + 'static>);
26impl Diagnostic for DiagnosticError {}
27
28impl From<Box<dyn std::error::Error + Send + Sync + 'static>> for DiagnosticError {
29 fn from(value: Box<dyn std::error::Error + Send + Sync + 'static>) -> Self {
30 Self(value)
31 }
32}
33
34#[derive(Debug, Error, Diagnostic)]
37#[error(transparent)]
38pub struct AnyhowError(#[from] anyhow::Error);
39
40#[derive(Debug, Clone, Error)]
45#[error("{title}: {message}")]
46pub struct TraceableError {
47 title: String,
48 kind: DiagnosticKind,
49 message: String,
50 severity: Severity,
51 #[allow(clippy::rc_buffer)]
52 src: Option<Arc<String>>,
53 label: SourceSpan,
54 help: Option<String>,
55 url: Option<String>,
56 hide_stack: Option<bool>,
57}
58
59impl Diagnostic for TraceableError {
60 fn severity(&self) -> Option<Severity> {
61 Some(self.severity)
62 }
63
64 fn help(&self) -> Option<Box<dyn Display + '_>> {
65 self
66 .help
67 .as_ref()
68 .map(Box::new)
69 .map(|c| c as Box<dyn Display>)
70 }
71
72 fn url(&self) -> Option<Box<dyn Display + '_>> {
73 self
74 .url
75 .as_ref()
76 .map(Box::new)
77 .map(|c| c as Box<dyn Display>)
78 }
79
80 fn source_code(&self) -> Option<&dyn SourceCode> {
81 self.src.as_ref().map(|s| &**s as &dyn SourceCode)
82 }
83
84 fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> {
85 use miette::macro_helpers::{OptionalWrapper, ToOption};
86 std::option::Option::Some(Box::new(
87 vec![
88 OptionalWrapper::<SourceSpan>::new()
89 .to_option(&self.label)
90 .map(|label| miette::LabeledSpan::new_with_span(None, *label)),
91 ]
92 .into_iter()
93 .filter(Option::is_some)
94 .flatten(),
95 ))
96 }
97}
98
99impl TraceableError {
100 pub fn with_severity(mut self, severity: impl Into<Severity>) -> Self {
101 self.severity = severity.into();
102 self
103 }
104
105 pub fn with_kind(mut self, kind: DiagnosticKind) -> Self {
106 self.kind = kind;
107 self
108 }
109
110 pub fn with_help(mut self, help: Option<impl Into<String>>) -> Self {
111 self.help = help.map(|h| h.into());
112 self
113 }
114
115 pub fn with_url(mut self, url: Option<impl Into<String>>) -> Self {
116 self.url = url.map(|u| u.into());
117 self
118 }
119
120 pub fn with_hide_stack(mut self, hide_stack: Option<bool>) -> Self {
121 self.hide_stack = hide_stack;
122 self
123 }
124
125 pub fn from_source_file(
126 source_file: &SourceFile,
127 start: usize,
128 end: usize,
129 title: String,
130 message: String,
131 ) -> Self {
132 Self::from_arc_string(
133 Some(source_file.src.clone().into_string().into()),
134 start,
135 end,
136 title,
137 message,
138 )
139 }
140
141 pub fn from_file(
142 file_src: String,
143 start: usize,
144 end: usize,
145 title: String,
146 message: String,
147 ) -> Self {
148 Self::from_arc_string(Some(Arc::new(file_src)), start, end, title, message)
149 }
150 pub fn from_lazy_file(start: usize, end: usize, title: String, message: String) -> Self {
152 Self::from_arc_string(None, start, end, title, message)
153 }
154
155 pub fn from_arc_string(
156 src: Option<Arc<String>>,
157 start: usize,
158 end: usize,
159 title: String,
160 message: String,
161 ) -> Self {
162 Self {
163 title,
164 kind: Default::default(),
165 message,
166 severity: Default::default(),
167 src,
168 label: SourceSpan::new(start.into(), end.saturating_sub(start)),
169 help: None,
170 url: None,
171 hide_stack: None,
172 }
173 }
174
175 pub fn hide_stack(&self) -> Option<bool> {
176 self.hide_stack
177 }
178}
179
180#[derive(Debug, Default)]
184pub struct BatchErrors(pub Vec<miette::Error>);
185
186impl BatchErrors {
187 pub fn into_inner(self) -> Vec<miette::Error> {
188 self.0
189 }
190}
191
192impl From<BatchErrors> for Vec<crate::Diagnostic> {
193 fn from(value: BatchErrors) -> Self {
194 value.0.into_iter().map(crate::Diagnostic::from).collect()
195 }
196}
197
198impl From<miette::Error> for BatchErrors {
199 fn from(value: miette::Error) -> Self {
200 Self(vec![value])
201 }
202}
203
204impl From<Vec<miette::Error>> for BatchErrors {
205 fn from(value: Vec<miette::Error>) -> Self {
206 Self(value)
207 }
208}
209
210#[macro_export]
211macro_rules! impl_diagnostic_transparent {
212 (code = $value:expr, $ty:ty) => {
213 impl miette::Diagnostic for $ty {
214 fn code<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
215 Some(Box::new($value))
216 }
217
218 fn severity(&self) -> Option<miette::Severity> {
219 self.0.severity()
220 }
221
222 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
223 self.0.help()
224 }
225
226 fn url<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
227 self.0.url()
228 }
229
230 fn source_code(&self) -> Option<&dyn miette::SourceCode> {
231 self.0.source_code()
232 }
233
234 fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
235 self.0.labels()
236 }
237
238 fn related<'a>(
239 &'a self,
240 ) -> Option<Box<dyn Iterator<Item = &'a dyn miette::Diagnostic> + 'a>> {
241 self.0.related()
242 }
243
244 fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
245 self.0.diagnostic_source()
246 }
247 }
248 };
249 ($ty:ty) => {
250 impl miette::Diagnostic for $ty {
251 fn code<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
252 self.0.code()
253 }
254
255 fn severity(&self) -> Option<miette::Severity> {
256 self.0.severity()
257 }
258
259 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
260 self.0.help()
261 }
262
263 fn url<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
264 self.0.url()
265 }
266
267 fn source_code(&self) -> Option<&dyn miette::SourceCode> {
268 self.0.source_code()
269 }
270
271 fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
272 self.0.labels()
273 }
274
275 fn related<'a>(
276 &'a self,
277 ) -> Option<Box<dyn Iterator<Item = &'a dyn miette::Diagnostic> + 'a>> {
278 self.0.related()
279 }
280
281 fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
282 self.0.diagnostic_source()
283 }
284 }
285 };
286}
287
288impl_diagnostic_transparent!(InternalError);
289
290#[macro_export]
291macro_rules! impl_error_transparent {
292 ($ty:ty) => {
293 impl std::error::Error for ModuleBuildError {
294 fn source(&self) -> ::core::option::Option<&(dyn std::error::Error + 'static)> {
295 std::error::Error::source(<Error as AsRef<dyn std::error::Error>>::as_ref(&self.0))
296 }
297 }
298
299 #[allow(unused_qualifications)]
300 impl ::core::fmt::Display for ModuleBuildError {
301 #[allow(clippy::used_underscore_binding)]
302 fn fmt(&self, __formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
303 ::core::fmt::Display::fmt(&self.0, __formatter)
304 }
305 }
306 };
307}
308
309#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
310pub enum DiagnosticKind {
311 JavaScript,
312 Typescript,
313 Jsx,
314 Tsx,
315 Scss,
316 Css,
317 #[default]
318 Internal,
319 Io,
320 Json,
321 Html,
322}
323
324impl std::fmt::Display for DiagnosticKind {
327 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
328 match self {
329 DiagnosticKind::JavaScript => write!(f, "javascript"),
330 DiagnosticKind::Typescript => write!(f, "typescript"),
331 DiagnosticKind::Jsx => write!(f, "jsx"),
332 DiagnosticKind::Tsx => write!(f, "tsx"),
333 DiagnosticKind::Scss => write!(f, "scss"),
334 DiagnosticKind::Css => write!(f, "css"),
335 DiagnosticKind::Internal => write!(f, "internal"),
336 DiagnosticKind::Io => write!(f, "io"),
337 DiagnosticKind::Json => write!(f, "json"),
338 DiagnosticKind::Html => write!(f, "html"),
339 }
340 }
341}
342
343fn _assert() {
344 fn _assert_send_sync<T: Send + Sync>() {}
345 _assert_send_sync::<InternalError>();
346 _assert_send_sync::<DiagnosticError>();
347}
348
349#[derive(Debug, Error, Diagnostic)]
351#[diagnostic()]
352#[error("{reason}\n{backtrace}")]
353pub struct NodeError {
354 pub reason: String,
355 pub stack: Option<String>,
356 pub backtrace: String,
357 pub hide_stack: Option<bool>,
358}