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 handler = to_pretty_handler(color);
91 self.0
92 .lock()
93 .expect("Failed to access the diagnostics lock")
94 .iter()
95 .map(|d| d.to_pretty_string(cm, skip_filename, &handler))
96 .collect::<Vec<String>>()
97 }
98
99 pub fn contains_error(&self) -> bool {
104 self.0
105 .lock()
106 .expect("Failed to access the diagnostics lock")
107 .iter()
108 .any(|d| d.level == swc_common::errors::Level::Error)
109 }
110}
111
112#[derive(Debug, Clone, Copy)]
113pub struct HandlerOpts {
114 pub color: ColorConfig,
117
118 pub skip_filename: bool,
120}
121
122impl Default for HandlerOpts {
123 fn default() -> Self {
124 Self {
125 color: ColorConfig::Auto,
126 skip_filename: false,
127 }
128 }
129}
130
131pub fn to_pretty_handler(color: ColorConfig) -> GraphicalReportHandler {
132 match color {
133 ColorConfig::Auto => {
134 if cfg!(target_arch = "wasm32") {
135 return to_pretty_handler(ColorConfig::Always).with_context_lines(3);
136 }
137
138 static ENABLE: Lazy<bool> =
139 Lazy::new(|| !env::var("NO_COLOR").map(|s| s == "1").unwrap_or(false));
140
141 if *ENABLE {
142 to_pretty_handler(ColorConfig::Always)
143 } else {
144 to_pretty_handler(ColorConfig::Never)
145 }
146 }
147 ColorConfig::Always => GraphicalReportHandler::default(),
148 ColorConfig::Never => GraphicalReportHandler::default().with_theme(GraphicalTheme::none()),
149 }
150 .with_context_lines(3)
151 .with_wrap_lines(false)
152 .with_break_words(false)
153}
154
155pub fn try_with_handler<F, Ret>(
158 cm: Lrc<SourceMap>,
159 config: HandlerOpts,
160 op: F,
161) -> Result<Ret, TWithDiagnosticArray<anyhow::Error>>
162where
163 F: FnOnce(&Handler) -> Result<Ret, anyhow::Error>,
164{
165 try_with_handler_inner(cm, config, op)
166}
167
168fn try_with_handler_inner<F, Ret>(
169 cm: Lrc<SourceMap>,
170 config: HandlerOpts,
171 op: F,
172) -> Result<Ret, TWithDiagnosticArray<anyhow::Error>>
173where
174 F: FnOnce(&Handler) -> Result<Ret, anyhow::Error>,
175{
176 let diagnostics = ThreadSafetyDiagnostics::default();
177
178 let emitter: Box<dyn Emitter> = Box::new(ErrorEmitter {
179 diagnostics: diagnostics.clone(),
180 cm: cm.clone(),
181 opts: config,
182 });
183
184 let handler = Handler::with_emitter(true, false, emitter);
185
186 let ret = HANDLER.set(&handler, || op(&handler));
187
188 match ret {
189 Ok(ret) => {
190 if diagnostics.contains_error() {
191 Err(diagnostics.as_array(cm.clone(), config))
192 } else {
193 Ok(ret)
194 }
195 }
196 Err(err) => Err(diagnostics.as_array_with_anyhow(err, cm.clone(), config)),
197 }
198}