swc_error_reporters/
handler.rs1use std::{
2 env,
3 fmt::Debug,
4 mem::take,
5 sync::{Arc, Mutex},
6};
7
8use miette::{GraphicalReportHandler, GraphicalTheme};
9use once_cell::sync::Lazy;
10use swc_common::{
11 errors::{ColorConfig, Diagnostic, Emitter, Handler, HANDLER},
12 sync::Lrc,
13 SourceMap,
14};
15
16use crate::{diagnostic::ToPrettyDiagnostic, ErrorEmitter, TWithDiagnosticArray};
17
18#[derive(Default, Clone)]
19pub struct ThreadSafetyDiagnostics(Arc<Mutex<Vec<Diagnostic>>>);
21
22impl ThreadSafetyDiagnostics {
23 pub fn push(&mut self, d: Diagnostic) {
25 self.0
26 .lock()
27 .expect("Failed to access the diagnostics lock")
28 .push(d);
29 }
30
31 pub fn take(&mut self) -> Vec<Diagnostic> {
33 take(
34 &mut *self
35 .0
36 .lock()
37 .expect("Failed to access the diagnostics lock"),
38 )
39 }
40
41 pub fn as_array(
42 mut self,
43 cm: Lrc<SourceMap>,
44 opts: HandlerOpts,
45 ) -> TWithDiagnosticArray<anyhow::Error> {
46 let diagnostics = self.take();
47
48 TWithDiagnosticArray {
49 inner: None,
50 diagnostics,
51 cm,
52 report_opts: opts,
53 }
54 }
55
56 pub fn as_array_with_anyhow(
57 mut self,
58 inner: anyhow::Error,
59 cm: Lrc<SourceMap>,
60 opts: HandlerOpts,
61 ) -> TWithDiagnosticArray<anyhow::Error> {
62 let diagnostics = self.take();
63
64 TWithDiagnosticArray {
65 inner: Some(inner),
66 diagnostics,
67 cm,
68 report_opts: opts,
69 }
70 }
71
72 pub fn to_pretty_string(
85 &self,
86 cm: &SourceMap,
87 skip_filename: bool,
88 color: ColorConfig,
89 ) -> Vec<String> {
90 let diagnostics = self
91 .0
92 .lock()
93 .expect("Failed to access the diagnostics lock");
94
95 if diagnostics.is_empty() {
97 return Vec::new();
98 }
99
100 let handler = to_pretty_handler(color);
101 diagnostics
102 .iter()
103 .map(|d| d.to_pretty_string(cm, skip_filename, &handler))
104 .collect::<Vec<String>>()
105 }
106
107 pub fn contains_error(&self) -> bool {
112 self.0
113 .lock()
114 .expect("Failed to access the diagnostics lock")
115 .iter()
116 .any(|d| d.level == swc_common::errors::Level::Error)
117 }
118}
119
120#[derive(Debug, Clone, Copy)]
121pub struct HandlerOpts {
122 pub color: ColorConfig,
125
126 pub skip_filename: bool,
128}
129
130impl Default for HandlerOpts {
131 fn default() -> Self {
132 Self {
133 color: ColorConfig::Auto,
134 skip_filename: false,
135 }
136 }
137}
138
139pub fn to_pretty_handler(color: ColorConfig) -> GraphicalReportHandler {
140 match color {
141 ColorConfig::Auto => {
142 if cfg!(target_arch = "wasm32") {
143 return to_pretty_handler(ColorConfig::Always).with_context_lines(3);
144 }
145
146 static ENABLE: Lazy<bool> =
147 Lazy::new(|| !env::var("NO_COLOR").map(|s| s == "1").unwrap_or(false));
148
149 if *ENABLE {
150 to_pretty_handler(ColorConfig::Always)
151 } else {
152 to_pretty_handler(ColorConfig::Never)
153 }
154 }
155 ColorConfig::Always => GraphicalReportHandler::default(),
156 ColorConfig::Never => GraphicalReportHandler::default().with_theme(GraphicalTheme::none()),
157 }
158 .with_context_lines(3)
159 .with_wrap_lines(false)
160 .with_break_words(false)
161}
162
163pub fn try_with_handler<F, Ret>(
166 cm: Lrc<SourceMap>,
167 config: HandlerOpts,
168 op: F,
169) -> Result<Ret, TWithDiagnosticArray<anyhow::Error>>
170where
171 F: FnOnce(&Handler) -> Result<Ret, anyhow::Error>,
172{
173 try_with_handler_inner(cm, config, op)
174}
175
176fn try_with_handler_inner<F, Ret>(
177 cm: Lrc<SourceMap>,
178 config: HandlerOpts,
179 op: F,
180) -> Result<Ret, TWithDiagnosticArray<anyhow::Error>>
181where
182 F: FnOnce(&Handler) -> Result<Ret, anyhow::Error>,
183{
184 let diagnostics = ThreadSafetyDiagnostics::default();
185
186 let emitter: Box<dyn Emitter> = Box::new(ErrorEmitter {
187 diagnostics: diagnostics.clone(),
188 cm: cm.clone(),
189 opts: config,
190 });
191
192 let handler = Handler::with_emitter(true, false, emitter);
193
194 let ret = HANDLER.set(&handler, || op(&handler));
195
196 match ret {
197 Ok(ret) => {
198 if diagnostics.contains_error() {
199 Err(diagnostics.as_array(cm.clone(), config))
200 } else {
201 Ok(ret)
202 }
203 }
204 Err(err) => Err(diagnostics.as_array_with_anyhow(err, cm.clone(), config)),
205 }
206}