diff --git a/Cargo.lock b/Cargo.lock index 4a95aa77d..fa2e01526 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1226,7 +1226,10 @@ dependencies = [ "oxc_allocator", "oxc_ast", "oxc_diagnostics", + "oxc_linter", + "oxc_minifier", "oxc_parser", + "oxc_printer", "oxc_semantic", "serde", "serde-wasm-bindgen", diff --git a/crates/oxc_wasm/Cargo.toml b/crates/oxc_wasm/Cargo.toml index 9d0348aab..e94a5761a 100644 --- a/crates/oxc_wasm/Cargo.toml +++ b/crates/oxc_wasm/Cargo.toml @@ -19,6 +19,9 @@ oxc_ast = { workspace = true, features = ["serde"] } oxc_diagnostics = { workspace = true } oxc_parser = { workspace = true } oxc_semantic = { workspace = true } +oxc_linter = { workspace = true } +oxc_printer = { workspace = true } +oxc_minifier = { workspace = true } miette = { workspace = true, features = ["fancy-no-backtrace"] } serde = { workspace = true } diff --git a/crates/oxc_wasm/src/lib.rs b/crates/oxc_wasm/src/lib.rs index dd456ddab..a933b1478 100644 --- a/crates/oxc_wasm/src/lib.rs +++ b/crates/oxc_wasm/src/lib.rs @@ -1,8 +1,14 @@ +use std::{cell::RefCell, rc::Rc}; + use oxc_allocator::Allocator; use oxc_ast::SourceType; use oxc_diagnostics::Error; +use oxc_linter::Linter; +use oxc_minifier::{Minifier, MinifierOptions}; use oxc_parser::Parser; +use oxc_printer::{Printer, PrinterOptions}; use oxc_semantic::SemanticBuilder; +use serde::ser::Serialize; use wasm_bindgen::prelude::*; #[wasm_bindgen(start)] @@ -12,52 +18,156 @@ pub fn main() { } #[wasm_bindgen] +#[derive(Default)] pub struct Oxc { - diagnostics: Vec, + source_text: String, + + options: OxcOptions, + + ast: JsValue, + + printed_text: JsValue, + + diagnostics: RefCell>, + + serializer: serde_wasm_bindgen::Serializer, +} + +#[wasm_bindgen] +#[derive(Default, Clone, Copy)] +pub struct OxcOptions { + pub parser: Option, + pub linter: Option, + pub minifier: Option, + pub printer: Option, +} + +#[wasm_bindgen] +#[derive(Default, Clone, Copy)] +pub struct OxcParserOptions { + #[wasm_bindgen(js_name = allowReturnOutsideFunction)] + pub allow_return_outside_function: bool, +} + +#[wasm_bindgen] +#[derive(Default, Clone, Copy)] +pub struct OxcLinterOptions; + +#[wasm_bindgen] +#[derive(Default, Clone, Copy)] +pub struct OxcMinifierOptions; + +#[wasm_bindgen] +#[derive(Default, Clone, Copy)] +pub struct OxcPrinterOptions { + mangle: bool, + #[wasm_bindgen(js_name = minifyWhitespace)] + pub minify_whitespace: bool, + pub indentation: u8, } #[wasm_bindgen] impl Oxc { #[wasm_bindgen(constructor)] - #[allow(clippy::new_without_default)] #[must_use] pub fn new() -> Self { - Self { diagnostics: vec![] } + Self { serializer: serde_wasm_bindgen::Serializer::json_compatible(), ..Self::default() } } + #[wasm_bindgen(js_name = setOptions)] + pub fn set_options(&mut self, options: OxcOptions) { + self.diagnostics = RefCell::default(); + self.options = options; + } + + #[wasm_bindgen(js_name = setSourceText)] + pub fn set_source_text(&mut self, text: String) { + self.diagnostics = RefCell::default(); + self.source_text = text; + } + + /// Returns AST in JSON #[wasm_bindgen] - #[must_use] - pub fn diagnostics(&self) -> JsValue { + pub fn ast(&self) -> JsValue { + self.ast.clone() + } + + /// Returns String + #[wasm_bindgen] + pub fn printed_text(&self) -> JsValue { + self.printed_text.clone() + } + + /// Returns Array of String + #[wasm_bindgen] + pub fn diagnostics(&self) -> Result { let diagnostics = self .diagnostics + .borrow() .iter() .map(|error| format!("{error:?}")) - .collect::>() - .join("\n"); - JsValue::from_str(&diagnostics) + .collect::>(); + diagnostics.serialize(&self.serializer) } - /// # Panics + /// # Errors + /// Serde serialization error #[wasm_bindgen] - #[must_use] - pub fn parse(&mut self, source_text: &str) -> JsValue { - self.diagnostics = vec![]; + pub fn run(&mut self) -> Result<(), serde_wasm_bindgen::Error> { + let Some(parser_options) = &self.options.parser else { + return Ok(()) + }; let allocator = Allocator::default(); + let source_text = &self.source_text; let source_type = SourceType::from_path("test.tsx").unwrap_or_default(); let ret = Parser::new(&allocator, source_text, source_type) - .allow_return_outside_function(true) + .allow_return_outside_function(parser_options.allow_return_outside_function) .parse(); - self.diagnostics.extend(ret.errors); + self.save_diagnostics(ret.errors); let program = allocator.alloc(ret.program); + + if self.options.minifier.is_some() { + let minifier_options = MinifierOptions::default(); + Minifier::new(&allocator, minifier_options).build(program); + } + + self.ast = program.serialize(&self.serializer)?; + let semantic_ret = SemanticBuilder::new(source_text, source_type, &ret.trivias) .with_module_record_builder(true) .with_check_syntax_error(true) .build(program); - self.diagnostics.extend(semantic_ret.errors); + let semantic = Rc::new(semantic_ret.semantic); + self.save_diagnostics(semantic_ret.errors); - serde_wasm_bindgen::to_value(program).unwrap() + if self.options.linter.is_some() { + let linter_ret = Linter::new().run(&semantic); + let diagnostics = linter_ret.into_iter().map(|e| e.error).collect(); + self.save_diagnostics(diagnostics); + } + + if let Some(o) = &self.options.printer { + let printer_options = PrinterOptions { + minify_whitespace: o.minify_whitespace, + indentation: o.indentation, + }; + let printed = Printer::new(source_text.len(), printer_options) + .with_symbol_table(&semantic.symbols(), o.mangle) + .build(program); + self.printed_text = printed.serialize(&self.serializer)?; + } + + Ok(()) + } + + fn save_diagnostics(&self, diagnostics: Vec) { + self.diagnostics.borrow_mut().extend( + diagnostics + .into_iter() + .map(|diagnostic| diagnostic.with_source_code(self.source_text.clone())), + ); } }