From 441a19fbaf4d0207e215c68815434696252a3ca2 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Thu, 11 Apr 2024 18:53:33 -0400 Subject: [PATCH 1/2] refactor: add more error types --- Cargo.lock | 21 +++++++ Cargo.toml | 1 + src/emit.rs | 20 +++++- src/transpiling/mod.rs | 135 +++++++++++++++++++++++++++++------------ 4 files changed, 135 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8644ff4..2bd6241 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -251,6 +251,7 @@ dependencies = [ "swc_visit", "swc_visit_macros", "text_lines", + "thiserror", "unicode-width", "url", ] @@ -1597,6 +1598,26 @@ dependencies = [ "serde", ] +[[package]] +name = "thiserror" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tinyvec" version = "1.6.0" diff --git a/Cargo.toml b/Cargo.toml index 33ac210..bd16c31 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,6 +75,7 @@ swc_macros_common = { version = "=0.3.9", optional = true } swc_trace_macro = { version = "=0.1.3", optional = true } swc_visit = { version = "=0.5.9", optional = true } swc_visit_macros = { version = "=0.5.10", optional = true } +thiserror = "1.0.58" [dev-dependencies] pretty_assertions = "1.3.0" diff --git a/src/emit.rs b/src/emit.rs index bfeb145..0322b9d 100644 --- a/src/emit.rs +++ b/src/emit.rs @@ -2,6 +2,7 @@ use anyhow::Result; use base64::Engine; +use thiserror::Error; use crate::swc::ast::Program; use crate::swc::codegen::text_writer::JsWriter; @@ -49,6 +50,16 @@ pub struct EmittedSource { pub source_map: Option, } +#[derive(Debug, Error)] +pub enum EmitError { + #[error(transparent)] + Utf8(#[from] std::string::FromUtf8Error), + #[error(transparent)] + SwcEmit(anyhow::Error), + #[error(transparent)] + SourceMap(anyhow::Error), +} + /// Emits the program as a string of JavaScript code, possibly with the passed /// comments, and optionally also a source map. pub fn emit( @@ -56,7 +67,7 @@ pub fn emit( comments: &dyn crate::swc::common::comments::Comments, source_map: &SourceMap, emit_options: &EmitOptions, -) -> Result { +) -> Result { let source_map = source_map.inner(); let mut src_map_buf = vec![]; let mut buf = vec![]; @@ -79,7 +90,9 @@ pub fn emit( cm: source_map.clone(), wr: writer, }; - program.emit_with(&mut emitter)?; + program + .emit_with(&mut emitter) + .map_err(|e| EmitError::SwcEmit(e.into()))?; } let mut src = String::from_utf8(buf)?; @@ -92,7 +105,8 @@ pub fn emit( }; source_map .build_source_map_with_config(&src_map_buf, None, source_map_config) - .to_writer(&mut buf)?; + .to_writer(&mut buf) + .map_err(|e| EmitError::SourceMap(e.into()))?; if emit_options.source_map == SourceMapOption::Inline { if !src.ends_with('\n') { diff --git a/src/transpiling/mod.rs b/src/transpiling/mod.rs index afe8bea..5ad245b 100644 --- a/src/transpiling/mod.rs +++ b/src/transpiling/mod.rs @@ -2,10 +2,9 @@ use std::rc::Rc; -use anyhow::anyhow; -use anyhow::bail; use anyhow::Result; use swc_ecma_visit::as_folder; +use thiserror::Error; use crate::emit; use crate::swc::ast::Program; @@ -24,8 +23,10 @@ use crate::swc::transforms::react; use crate::swc::transforms::resolver; use crate::swc::transforms::typescript; use crate::swc::visit::FoldWith; +use crate::EmitError; use crate::EmitOptions; use crate::EmittedSource; +use crate::ModuleSpecifier; use crate::ParseDiagnostic; use crate::ParseDiagnosticsError; use crate::ParsedSource; @@ -36,6 +37,21 @@ use std::cell::RefCell; mod jsx_precompile; mod transforms; +#[derive(Debug, Error)] +pub enum TranspileError { + #[error("Can't use TranspileOptions::use_decorators_proposal and TranspileOptions::use_ts_decorators together.")] + DecoratorOptionsConflict, + /// Parse errors that prevent transpiling. + #[error(transparent)] + ParseErrors(#[from] ParseDiagnosticsError), + #[error(transparent)] + FoldProgram(#[from] FoldProgramError), + #[error("{0}")] + EmitDiagnostic(String), + #[error(transparent)] + Emit(#[from] EmitError), +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum ImportsNotUsedAsValues { Remove, @@ -150,37 +166,52 @@ impl ParsedSource { &self, transpile_options: &TranspileOptions, emit_options: &EmitOptions, - ) -> Result { - if transpile_options.use_decorators_proposal - && transpile_options.use_ts_decorators - { - bail!("Can't use TranspileOptions::use_decorators_proposal and TranspileOptions::use_ts_decorators together."); - } - + ) -> Result { let program = (*self.program()).clone(); - - let source_map = SourceMap::single( + transpile( self.specifier().clone(), self.text_info().text_str().to_string(), - ); - - // we need the comments to be mutable, so make it single threaded - let comments = self.comments().as_single_threaded(); - let globals = Globals::new(); - let program = crate::swc::common::GLOBALS.set(&globals, || { - let top_level_mark = Mark::fresh(Mark::root()); - fold_program( - program, - transpile_options, - &source_map, - &comments, - top_level_mark, - self.diagnostics(), - ) - })?; + program, + // we need the comments to be mutable, so make it single threaded + self.comments().as_single_threaded(), + transpile_options, + emit_options, + self.diagnostics(), + ) + } +} - emit(&program, &comments, &source_map, emit_options) +fn transpile( + specifier: ModuleSpecifier, + source: String, + program: Program, + comments: SingleThreadedComments, + transpile_options: &TranspileOptions, + emit_options: &EmitOptions, + diagnostics: &[ParseDiagnostic], +) -> Result { + if transpile_options.use_decorators_proposal + && transpile_options.use_ts_decorators + { + return Err(TranspileError::DecoratorOptionsConflict); } + + let source_map = SourceMap::single(specifier, source); + + let globals = Globals::new(); + let program = crate::swc::common::GLOBALS.set(&globals, || { + let top_level_mark = Mark::fresh(Mark::root()); + fold_program( + program, + transpile_options, + &source_map, + &comments, + top_level_mark, + diagnostics, + ) + })?; + + Ok(emit(&program, &comments, &source_map, emit_options)?) } #[derive(Default, Clone)] @@ -205,6 +236,14 @@ impl crate::swc::common::errors::Emitter for DiagnosticCollector { } } +#[derive(Debug, Error)] +pub enum FoldProgramError { + #[error(transparent)] + ParseDiagnostics(#[from] ParseDiagnosticsError), + #[error(transparent)] + Swc(#[from] SwcFoldDiagnosticsError), +} + /// Low level function for transpiling a program. pub fn fold_program( program: Program, @@ -213,7 +252,7 @@ pub fn fold_program( comments: &SingleThreadedComments, top_level_mark: Mark, diagnostics: &[ParseDiagnostic], -) -> Result { +) -> Result { ensure_no_fatal_diagnostics(diagnostics)?; let unresolved_mark = Mark::new(); @@ -312,26 +351,44 @@ pub fn fold_program( }) }); - let diagnostics = diagnostics_cell.borrow(); - ensure_no_fatal_swc_diagnostics(source_map, diagnostics.iter())?; + let mut diagnostics = diagnostics_cell.borrow_mut(); + let diagnostics = std::mem::take(&mut *diagnostics); + ensure_no_fatal_swc_diagnostics(source_map, diagnostics.into_iter())?; Ok(result) } -fn ensure_no_fatal_swc_diagnostics<'a>( +#[derive(Debug)] +pub struct SwcFoldDiagnosticsError(Vec); + +impl std::error::Error for SwcFoldDiagnosticsError {} + +impl std::fmt::Display for SwcFoldDiagnosticsError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for (i, diagnostic) in self.0.iter().enumerate() { + if i > 0 { + write!(f, "\n\n")?; + } + + write!(f, "{}", diagnostic)? + } + + Ok(()) + } +} + +fn ensure_no_fatal_swc_diagnostics( source_map: &SourceMap, - diagnostics: impl Iterator, -) -> Result<()> { + diagnostics: impl Iterator, +) -> Result<(), SwcFoldDiagnosticsError> { let fatal_diagnostics = diagnostics - .filter(|d| is_fatal_swc_diagnostic(d)) + .filter(is_fatal_swc_diagnostic) .collect::>(); if !fatal_diagnostics.is_empty() { - Err(anyhow!( - "{}", + Err(SwcFoldDiagnosticsError( fatal_diagnostics .iter() .map(|d| format_swc_diagnostic(source_map, d)) - .collect::>() - .join("\n\n") + .collect::>(), )) } else { Ok(()) From e21af6e1469056efec7fe735a16cdb71117ff7b7 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Thu, 11 Apr 2024 19:00:25 -0400 Subject: [PATCH 2/2] Revert this code --- src/transpiling/mod.rs | 66 ++++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 41 deletions(-) diff --git a/src/transpiling/mod.rs b/src/transpiling/mod.rs index 5ad245b..6b8222f 100644 --- a/src/transpiling/mod.rs +++ b/src/transpiling/mod.rs @@ -26,7 +26,6 @@ use crate::swc::visit::FoldWith; use crate::EmitError; use crate::EmitOptions; use crate::EmittedSource; -use crate::ModuleSpecifier; use crate::ParseDiagnostic; use crate::ParseDiagnosticsError; use crate::ParsedSource; @@ -167,51 +166,36 @@ impl ParsedSource { transpile_options: &TranspileOptions, emit_options: &EmitOptions, ) -> Result { + if transpile_options.use_decorators_proposal + && transpile_options.use_ts_decorators + { + return Err(TranspileError::DecoratorOptionsConflict); + } + let program = (*self.program()).clone(); - transpile( + + let source_map = SourceMap::single( self.specifier().clone(), self.text_info().text_str().to_string(), - program, - // we need the comments to be mutable, so make it single threaded - self.comments().as_single_threaded(), - transpile_options, - emit_options, - self.diagnostics(), - ) - } -} - -fn transpile( - specifier: ModuleSpecifier, - source: String, - program: Program, - comments: SingleThreadedComments, - transpile_options: &TranspileOptions, - emit_options: &EmitOptions, - diagnostics: &[ParseDiagnostic], -) -> Result { - if transpile_options.use_decorators_proposal - && transpile_options.use_ts_decorators - { - return Err(TranspileError::DecoratorOptionsConflict); - } + ); - let source_map = SourceMap::single(specifier, source); - - let globals = Globals::new(); - let program = crate::swc::common::GLOBALS.set(&globals, || { - let top_level_mark = Mark::fresh(Mark::root()); - fold_program( - program, - transpile_options, - &source_map, - &comments, - top_level_mark, - diagnostics, - ) - })?; + // we need the comments to be mutable, so make it single threaded + let comments = self.comments().as_single_threaded(); + let globals = Globals::new(); + let program = crate::swc::common::GLOBALS.set(&globals, || { + let top_level_mark = Mark::fresh(Mark::root()); + fold_program( + program, + transpile_options, + &source_map, + &comments, + top_level_mark, + self.diagnostics(), + ) + })?; - Ok(emit(&program, &comments, &source_map, emit_options)?) + Ok(emit(&program, &comments, &source_map, emit_options)?) + } } #[derive(Default, Clone)]