Skip to content

Commit 156c3b1

Browse files
GiveMe-A-Namekdy1
andcommitted
feat(errors): Integrate miette for enhanced diagnostic reporting (#10241)
# Description SWC api `try_with_handler` only return anyhow!(msg) Err, it'a will lose many message about error stack, level ..., To express more error message in swc, we can return `Vec<Diagnostic>` in `try_with_handler`; try_with_handler_diagnostic return a `Result<Ret, Vec<Diagnostic>>`. Later user can using `diagnostic.code` to distinguish what kind error is. - Added `miette` as a dependency for improved error handling and reporting. - Updated diagnostic handling to utilize `miette` for pretty printing errors. - Introduced a new `ThreadSafetyDiagnostics` to manage and store diagnostics. - Refactored emitters to support the new diagnostic structure and ensure compatibility with `miette`. - Enhanced error handling in various components to provide clearer and more informative error messages. # BREAKING CHANGE: The api `try_with_handler` would not return anyhow!(msg). It's will return WrapperDiagnostics as it's Err. Then you can take diagnostics from WrapperDiagnostics or get pretty string from it. ```diff - pub fn try_with_handler<F, Ret>(cm: Lrc<SourceMap>, config: HandlerOpts, op:F) -> Result<Ret, anyhow::error>; + pub fn try_with_handler<F, Ret>(cm: Lrc<SourceMap>, config: HandlerOpts, op:F) -> Result<Ret, WrapperDiagnostics>; ``` ```rust /// A wrapper around a value that also contains a list of diagnostics. /// It helps swc error system migrate to the new error reporting system. pub struct WrapperDiagnostics { diagnostics: Vec<SwcDignostic>, .... } impl WrapperDiagnostics { pub fn diagnostics(&self) -> &[Diagnostic] { &self.diagnostics } pub fn to_pretty_error(&self) -> anyhow::Error { let error_msg = self.to_pretty_string(); anyhow::anyhow!(error_msg) } pub fn to_pretty_string(&self) -> String { .... } } ``` ### formatting In SWC, the `Syntax Error` message formatting dependents anyhow. ```rust // input: Foo {} // https://play.swc.rs/?version=1.11.13&code=H4sIAAAAAAAAA3PLz1eorgUAR6TWygYAAAA%3D&config=H4sIAAAAAAAAA1WPSw7DIAwF9zkF8rrbdtE79BAWdSIifrKJVBTl7iUE0maH3xsz8jooBbNoeKq1PMsQkYX4nEsi2Sf8lARIOxTNJia49XaWvRrRCtVoOxpIyBOluiX3hoMNQajjLXPGmzH%2FC3VwkUnkCu4o%2BsnSVTc0JbjwXmrZDkk50qF%2FwA%2FqsvNjMPLqm4kXGrYvhlQioBQBAAA%3D x Expected ';', '}' or <eof> ,-[input.js:1:1] 1 | Foo {} : ^|^ ^ : `-- This is the expression part of an expression statement `---- Caused by: 0: failed to process js file 1: Syntax Error ``` So if we return `Vec<Diagnostic>` instead of `anyhow:Error`, The output formatting would have a litter breaking change. ```diff x Expected ';', '}' or <eof> ,-[input.js:1:1] 1 | Foo {} : ^|^ ^ : `-- This is the expression part of an expression statement `---- - Caused by: - 0: failed to process js file - 1: Syntax Error + x Syntax Error ``` # Related issue: - Closes #10192 --------- Co-authored-by: Donny/강동윤 <[email protected]>
1 parent 3f71776 commit 156c3b1

File tree

32 files changed

+865
-778
lines changed

32 files changed

+865
-778
lines changed

.changeset/sweet-days-notice.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
swc_error_reporters: major
3+
swc_common: minor
4+
---
5+
6+
enhance(errors_report): integrate miette for enhanced diagnostic reporting

bindings/binding_core_node/src/util.rs

+26-24
Original file line numberDiff line numberDiff line change
@@ -67,32 +67,34 @@ pub fn try_with<F, Ret>(
6767
where
6868
F: FnOnce(&Handler) -> Result<Ret, Error>,
6969
{
70-
GLOBALS.set(&Default::default(), || {
71-
try_with_handler(
72-
cm,
73-
swc_core::base::HandlerOpts {
74-
skip_filename,
75-
..Default::default()
76-
},
77-
|handler| {
78-
//
79-
let result = catch_unwind(AssertUnwindSafe(|| op(handler)));
70+
GLOBALS
71+
.set(&Default::default(), || {
72+
try_with_handler(
73+
cm,
74+
swc_core::base::HandlerOpts {
75+
skip_filename,
76+
..Default::default()
77+
},
78+
|handler| {
79+
//
80+
let result = catch_unwind(AssertUnwindSafe(|| op(handler)));
8081

81-
let p = match result {
82-
Ok(v) => return v,
83-
Err(v) => v,
84-
};
82+
let p = match result {
83+
Ok(v) => return v,
84+
Err(v) => v,
85+
};
8586

86-
if let Some(s) = p.downcast_ref::<String>() {
87-
Err(anyhow!("failed to handle: {}", s))
88-
} else if let Some(s) = p.downcast_ref::<&str>() {
89-
Err(anyhow!("failed to handle: {}", s))
90-
} else {
91-
Err(anyhow!("failed to handle with unknown panic message"))
92-
}
93-
},
94-
)
95-
})
87+
if let Some(s) = p.downcast_ref::<String>() {
88+
Err(anyhow!("failed to handle: {}", s))
89+
} else if let Some(s) = p.downcast_ref::<&str>() {
90+
Err(anyhow!("failed to handle: {}", s))
91+
} else {
92+
Err(anyhow!("failed to handle with unknown panic message"))
93+
}
94+
},
95+
)
96+
})
97+
.map_err(|e| e.to_pretty_error())
9698
}
9799

98100
// This was originally under swc_nodejs_common, but this is not a public

bindings/binding_core_wasm/__tests__/error.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const swc = require("../pkg");
22

3-
it("properly reports error", function () {
3+
it("properly reports error", () => {
44
expect(() => {
55
swc.transformSync("Foo {}", {});
66
}).toThrow("Syntax Error");

bindings/binding_html_node/src/util.rs

+1
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,5 @@ where
3232
}
3333
},
3434
)
35+
.map_err(|e| e.to_pretty_error())
3536
}

bindings/binding_minifier_node/src/util.rs

+26-24
Original file line numberDiff line numberDiff line change
@@ -60,32 +60,34 @@ pub fn try_with<F, Ret>(cm: Lrc<SourceMap>, skip_filename: bool, op: F) -> Resul
6060
where
6161
F: FnOnce(&Handler) -> Result<Ret, Error>,
6262
{
63-
GLOBALS.set(&Default::default(), || {
64-
try_with_handler(
65-
cm,
66-
HandlerOpts {
67-
skip_filename,
68-
..Default::default()
69-
},
70-
|handler| {
71-
//
72-
let result = catch_unwind(AssertUnwindSafe(|| op(handler)));
63+
GLOBALS
64+
.set(&Default::default(), || {
65+
try_with_handler(
66+
cm,
67+
HandlerOpts {
68+
skip_filename,
69+
..Default::default()
70+
},
71+
|handler| {
72+
//
73+
let result = catch_unwind(AssertUnwindSafe(|| op(handler)));
7374

74-
let p = match result {
75-
Ok(v) => return v,
76-
Err(v) => v,
77-
};
75+
let p = match result {
76+
Ok(v) => return v,
77+
Err(v) => v,
78+
};
7879

79-
if let Some(s) = p.downcast_ref::<String>() {
80-
Err(anyhow!("failed to handle: {}", s))
81-
} else if let Some(s) = p.downcast_ref::<&str>() {
82-
Err(anyhow!("failed to handle: {}", s))
83-
} else {
84-
Err(anyhow!("failed to handle with unknown panic message"))
85-
}
86-
},
87-
)
88-
})
80+
if let Some(s) = p.downcast_ref::<String>() {
81+
Err(anyhow!("failed to handle: {}", s))
82+
} else if let Some(s) = p.downcast_ref::<&str>() {
83+
Err(anyhow!("failed to handle: {}", s))
84+
} else {
85+
Err(anyhow!("failed to handle with unknown panic message"))
86+
}
87+
},
88+
)
89+
})
90+
.map_err(|e| e.to_pretty_error())
8991
}
9092

9193
// This was originally under swc_nodejs_common, but this is not a public

bindings/binding_typescript_wasm/src/lib.rs

+4-9
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use swc_common::{
1414
sync::Lrc,
1515
SourceMap, Span, GLOBALS,
1616
};
17-
use swc_error_reporters::{to_miette_source_code, to_miette_span, PrettyEmitterConfig};
17+
use swc_error_reporters::{convert_span, to_pretty_source_code};
1818
use swc_fast_ts_strip::{Options, TransformOutput};
1919
use wasm_bindgen::prelude::*;
2020
use wasm_bindgen_futures::{future_to_promise, js_sys::Promise};
@@ -125,20 +125,15 @@ where
125125
}
126126

127127
impl Emitter for JsonErrorWriter {
128-
fn emit(&mut self, db: &DiagnosticBuilder) {
128+
fn emit(&mut self, db: &mut DiagnosticBuilder) {
129129
let d = &**db;
130130

131131
let snippet = d.span.primary_span().and_then(|span| {
132132
let mut snippet = String::new();
133133
match self.reporter.render_report(
134134
&mut snippet,
135135
&Snippet {
136-
source_code: &to_miette_source_code(
137-
&self.cm,
138-
&PrettyEmitterConfig {
139-
skip_filename: true,
140-
},
141-
),
136+
source_code: &to_pretty_source_code(&self.cm, true),
142137
span,
143138
},
144139
) {
@@ -246,7 +241,7 @@ impl miette::Diagnostic for Snippet<'_> {
246241
fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
247242
Some(Box::new(once(LabeledSpan::new_with_span(
248243
None,
249-
to_miette_span(self.span),
244+
convert_span(self.span),
250245
))))
251246
}
252247
}

crates/binding_macros/src/wasm.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,11 @@ pub fn try_with_handler_globals<F, Ret>(
6060
where
6161
F: FnOnce(&Handler) -> Result<Ret, Error>,
6262
{
63-
GLOBALS.set(&Default::default(), || {
64-
swc::try_with_handler(cm, config, op)
65-
})
63+
GLOBALS
64+
.set(&Default::default(), || {
65+
swc::try_with_handler(cm, config, op)
66+
})
67+
.map_err(|e| e.to_pretty_error())
6668
}
6769

6870
/// Get global sourcemap

crates/dbg-swc/src/main.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::{env, path::PathBuf, str::FromStr, sync::Arc};
22

3-
use anyhow::{bail, Result};
3+
use anyhow::{bail, Error, Result};
44
use clap::{StructOpt, Subcommand};
55
use es::EsCommand;
66
use swc_common::{
@@ -51,7 +51,7 @@ fn init() -> Result<()> {
5151
Ok(())
5252
}
5353

54-
fn main() -> Result<()> {
54+
fn main() -> Result<(), Error> {
5555
init()?;
5656

5757
let cm = Arc::new(SourceMap::default());
@@ -104,7 +104,8 @@ fn main() -> Result<()> {
104104
})
105105
})
106106
},
107-
);
107+
)
108+
.map_err(|e| e.to_pretty_error());
108109
}
109110

110111
let args = AppArgs::parse();
@@ -118,9 +119,10 @@ fn main() -> Result<()> {
118119
|handler| {
119120
GLOBALS.set(&Globals::default(), || {
120121
HANDLER.set(handler, || match args.cmd {
121-
Cmd::Es(cmd) => cmd.run(cm),
122+
Cmd::Es(cmd) => cmd.run(cm.clone()),
122123
})
123124
})
124125
},
125126
)
127+
.map_err(|e| e.to_pretty_error())
126128
}

crates/swc/src/plugin.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use anyhow::{Context, Result};
1212
use atoms::Atom;
1313
use common::FileName;
1414
use serde::{Deserialize, Serialize};
15-
use swc_common::errors::HANDLER;
15+
use swc_common::errors::{DiagnosticId, HANDLER};
1616
use swc_ecma_ast::Pass;
1717
#[cfg(feature = "plugin")]
1818
use swc_ecma_ast::*;
@@ -184,7 +184,7 @@ impl Fold for RustPlugins {
184184
Ok(program) => program.expect_module(),
185185
Err(err) => {
186186
HANDLER.with(|handler| {
187-
handler.err(&err.to_string());
187+
handler.err_with_code(&err.to_string(), DiagnosticId::Error("plugin".into()));
188188
});
189189
Module::default()
190190
}
@@ -197,7 +197,7 @@ impl Fold for RustPlugins {
197197
Ok(program) => program.expect_script(),
198198
Err(err) => {
199199
HANDLER.with(|handler| {
200-
handler.err(&err.to_string());
200+
handler.err_with_code(&err.to_string(), DiagnosticId::Error("plugin".into()));
201201
});
202202
Script::default()
203203
}

crates/swc/tests/error_msg.rs

+1
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ fn fixture(input: PathBuf) {
8282
Ok(())
8383
},
8484
)
85+
.map_err(|e| e.to_pretty_error())
8586
.expect_err("should fail")
8687
});
8788

0 commit comments

Comments
 (0)