use std::{
fmt::Debug,
sync::{Arc, mpsc},
};
use rspack_error::{BatchErrors, Error, error};
use rspack_util::SpanExt;
use rustc_hash::FxHashSet as HashSet;
use swc_core::common::{
SourceMap, Span, Spanned,
errors::{Emitter, HANDLER, Handler},
};
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct EcmaError(String, Span);
#[derive(Debug)]
pub struct EcmaErrorsDeduped(Vec<EcmaError>);
impl IntoIterator for EcmaErrorsDeduped {
type Item = EcmaError;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl From<Vec<swc_core::ecma::parser::error::Error>> for EcmaErrorsDeduped {
fn from(value: Vec<swc_core::ecma::parser::error::Error>) -> Self {
Self(
value
.into_iter()
.map(|v| EcmaError(v.kind().msg().to_string(), v.span()))
.collect::<HashSet<_>>()
.into_iter()
.collect::<Vec<_>>(),
)
}
}
impl DedupEcmaErrors for Vec<swc_core::ecma::parser::error::Error> {
fn dedup_ecma_errors(self) -> EcmaErrorsDeduped {
EcmaErrorsDeduped::from(self)
}
}
pub trait DedupEcmaErrors {
fn dedup_ecma_errors(self) -> EcmaErrorsDeduped;
}
pub fn ecma_parse_error_deduped_to_rspack_error(
EcmaError(message, span): EcmaError,
source: String,
) -> Error {
Error::from_string(
Some(source),
span.real_lo() as usize,
span.real_hi() as usize,
"JavaScript parse error".into(),
message,
)
}
struct RspackErrorEmitter {
tx: mpsc::Sender<Error>,
source_map: Arc<SourceMap>,
title: String,
}
impl Emitter for RspackErrorEmitter {
fn emit(&mut self, db: &mut swc_core::common::errors::DiagnosticBuilder<'_>) {
let source_file_and_byte_pos = db
.span
.primary_span()
.map(|s| self.source_map.lookup_byte_offset(s.lo()));
if let Some(source_file_and_byte_pos) = source_file_and_byte_pos {
self
.tx
.send(Error::from_string(
Some(source_file_and_byte_pos.sf.src.clone().into_string()),
source_file_and_byte_pos.pos.0 as usize,
source_file_and_byte_pos.pos.0 as usize,
self.title.clone(),
db.message(),
))
.expect("Sender should drop after emit called");
} else {
self
.tx
.send(error!(db.message()))
.expect("Sender should drop after emit called");
}
}
}
pub fn with_rspack_error_handler<F, Ret>(
title: String,
cm: Arc<SourceMap>,
op: F,
) -> std::result::Result<Ret, BatchErrors>
where
F: FnOnce(&Handler) -> std::result::Result<Ret, BatchErrors>,
{
let (tx, rx) = mpsc::channel();
let emitter = RspackErrorEmitter {
title,
source_map: cm,
tx,
};
let handler = Handler::with_emitter(true, false, Box::new(emitter));
let ret = HANDLER.set(&handler, || op(&handler));
if handler.has_errors() {
drop(handler);
Err(BatchErrors(rx.into_iter().collect()))
} else {
ret
}
}