feat(wasm): integrate linter, minifier and printer (#313)

This commit is contained in:
Boshen 2023-04-24 10:17:13 +08:00 committed by GitHub
parent 97189c4e4c
commit 781c855c4d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 132 additions and 16 deletions

3
Cargo.lock generated
View file

@ -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",

View file

@ -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 }

View file

@ -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<Error>,
source_text: String,
options: OxcOptions,
ast: JsValue,
printed_text: JsValue,
diagnostics: RefCell<Vec<Error>>,
serializer: serde_wasm_bindgen::Serializer,
}
#[wasm_bindgen]
#[derive(Default, Clone, Copy)]
pub struct OxcOptions {
pub parser: Option<OxcParserOptions>,
pub linter: Option<OxcLinterOptions>,
pub minifier: Option<OxcMinifierOptions>,
pub printer: Option<OxcPrinterOptions>,
}
#[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<JsValue, serde_wasm_bindgen::Error> {
let diagnostics = self
.diagnostics
.borrow()
.iter()
.map(|error| format!("{error:?}"))
.collect::<Vec<String>>()
.join("\n");
JsValue::from_str(&diagnostics)
.collect::<Vec<String>>();
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<Error>) {
self.diagnostics.borrow_mut().extend(
diagnostics
.into_iter()
.map(|diagnostic| diagnostic.with_source_code(self.source_text.clone())),
);
}
}