Skip to content

Commit

Permalink
refactor(wasm): clean up code and add transform (#5299)
Browse files Browse the repository at this point in the history
Co-authored-by: Boshen <[email protected]>
  • Loading branch information
Boshen and Boshen authored Aug 28, 2024
1 parent 5380ecc commit 7fa2fa3
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 101 deletions.
203 changes: 111 additions & 92 deletions crates/oxc_wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,18 @@ mod options;

use std::{
cell::{Cell, RefCell},
path::PathBuf,
path::{Path, PathBuf},
rc::Rc,
};

use options::OxcOptions;
use oxc::{
allocator::Allocator,
ast::{ast::Program, CommentKind, Trivias, Visit},
codegen::{CodeGenerator, CodegenOptions},
diagnostics::Error,
minifier::{CompressOptions, Minifier, MinifierOptions},
parser::{ParseOptions, Parser},
semantic::{ScopeFlags, ScopeId, Semantic, SemanticBuilder},
parser::{ParseOptions, Parser, ParserReturn},
semantic::{ScopeFlags, ScopeId, ScopeTree, SemanticBuilder, SymbolTable},
span::SourceType,
transformer::{TransformOptions, Transformer},
};
Expand All @@ -28,6 +27,8 @@ use serde::Serialize;
use tsify::Tsify;
use wasm_bindgen::prelude::*;

use crate::options::{OxcOptions, OxcRunOptions};

#[wasm_bindgen(getter_with_clone)]
#[derive(Default, Tsify)]
#[serde(rename_all = "camelCase")]
Expand Down Expand Up @@ -146,19 +147,19 @@ impl Oxc {
run: run_options,
parser: parser_options,
linter: linter_options,
transformer: transform_options,
codegen: codegen_options,
minifier: minifier_options,
type_checking: type_checking_options,
} = options;
let run_options = run_options.unwrap_or_default();
let parser_options = parser_options.unwrap_or_default();
let _linter_options = linter_options.unwrap_or_default();
let _codegen_options = codegen_options.unwrap_or_default();
let minifier_options = minifier_options.unwrap_or_default();
let _type_checking_options = type_checking_options.unwrap_or_default();
let _codegen_options = codegen_options.unwrap_or_default();
let _transform_options = transform_options.unwrap_or_default();

let allocator = Allocator::default();
// let source_text = &self.source_text;

let path = PathBuf::from(
parser_options.source_filename.clone().unwrap_or_else(|| "test.tsx".to_string()),
);
Expand All @@ -171,117 +172,70 @@ impl Oxc {

let default_parser_options = ParseOptions::default();
let oxc_parser_options = ParseOptions {
parse_regular_expression: true,
allow_return_outside_function: parser_options
.allow_return_outside_function
.unwrap_or(default_parser_options.allow_return_outside_function),
preserve_parens: parser_options
.preserve_parens
.unwrap_or(default_parser_options.preserve_parens),
..default_parser_options
};
let ret = Parser::new(&allocator, source_text, source_type)
.with_options(oxc_parser_options)
.parse();

self.comments = Self::map_comments(source_text, &ret.trivias);

self.save_diagnostics(ret.errors.into_iter().map(Error::from).collect::<Vec<_>>());
let ParserReturn { mut program, errors, trivias, .. } =
Parser::new(&allocator, source_text, source_type)
.with_options(oxc_parser_options)
.parse();

self.ir = format!("{:#?}", ret.program.body);
self.comments = Self::map_comments(source_text, &trivias);
self.ir = format!("{:#?}", program.body);
self.ast = program.serialize(&self.serializer)?;

let program = allocator.alloc(ret.program);
self.save_diagnostics(errors.into_iter().map(Error::from).collect::<Vec<_>>());

let semantic_ret = SemanticBuilder::new(source_text, source_type)
.with_cfg(true)
.with_trivias(ret.trivias.clone())
.with_trivias(trivias.clone())
.with_check_syntax_error(true)
.build(program);
.build_module_record(path.clone(), &program)
.build(&program);

if run_options.syntax.unwrap_or_default() {
self.save_diagnostics(
semantic_ret.errors.into_iter().map(Error::from).collect::<Vec<_>>(),
);
}

let semantic = Rc::new(semantic_ret.semantic);
// Only lint if there are not syntax errors
if run_options.lint.unwrap_or_default() && self.diagnostics.borrow().is_empty() {
let linter_ret = Linter::default().run(&path, Rc::clone(&semantic));
let diagnostics = linter_ret.into_iter().map(|e| Error::from(e.error)).collect();
self.save_diagnostics(diagnostics);
}

self.ast = program.serialize(&self.serializer)?;
self.run_linter(&run_options, source_text, source_type, &path, &trivias, &program);

if run_options.prettier_format.unwrap_or_default()
|| run_options.prettier_ir.unwrap_or_default()
{
let ret = Parser::new(&allocator, source_text, source_type)
.with_options(ParseOptions { preserve_parens: false, ..oxc_parser_options })
.parse();
self.run_prettier(&run_options, source_text, source_type);

let mut prettier = Prettier::new(
&allocator,
source_text,
ret.trivias.clone(),
PrettierOptions::default(),
);
let (symbols, scopes) = semantic_ret.semantic.into_symbol_table_and_scope_tree();

if run_options.prettier_format.unwrap_or_default() {
self.prettier_formatted_text = prettier.build(&ret.program);
if run_options.scope.unwrap_or_default() || run_options.symbol.unwrap_or_default() {
if run_options.scope.unwrap_or_default() {
self.scope_text = Self::get_scope_text(&program, &symbols, &scopes);
}

if run_options.prettier_ir.unwrap_or_default() {
let prettier_doc = prettier.doc(&ret.program).to_string();
self.prettier_ir_text = {
let ret = Parser::new(&allocator, &prettier_doc, SourceType::default()).parse();
Prettier::new(
&allocator,
&prettier_doc,
ret.trivias,
PrettierOptions::default(),
)
.build(&ret.program)
};
if run_options.symbol.unwrap_or_default() {
self.symbols = symbols.serialize(&self.serializer)?;
}
}

if run_options.transform.unwrap_or_default() {
let (symbols, scopes) = SemanticBuilder::new(source_text, source_type)
.build(program)
.semantic
.into_symbol_table_and_scope_tree();
let options = TransformOptions::default();
let result = Transformer::new(
&allocator,
&path,
source_type,
source_text,
ret.trivias.clone(),
trivias.clone(),
options,
)
.build_with_symbols_and_scopes(symbols, scopes, program);
.build_with_symbols_and_scopes(symbols, scopes, &mut program);
if !result.errors.is_empty() {
let errors = result.errors.into_iter().map(Error::from).collect::<Vec<_>>();
self.save_diagnostics(errors);
}
}

if run_options.scope.unwrap_or_default() || run_options.symbol.unwrap_or_default() {
let semantic = SemanticBuilder::new(source_text, source_type)
.build_module_record(PathBuf::new(), program)
.build(program)
.semantic;
if run_options.scope.unwrap_or_default() {
self.scope_text = Self::get_scope_text(program, &semantic);
}
if run_options.symbol.unwrap_or_default() {
self.symbols = semantic.symbols().serialize(&self.serializer)?;
self.save_diagnostics(
result.errors.into_iter().map(Error::from).collect::<Vec<_>>(),
);
}
}

let program = allocator.alloc(program);

if minifier_options.compress.unwrap_or_default()
|| minifier_options.mangle.unwrap_or_default()
{
Expand All @@ -303,31 +257,96 @@ impl Oxc {
CompressOptions::all_false()
},
};
Minifier::new(options).build(&allocator, program);
Minifier::new(options).build(&allocator, &mut program);
}

self.codegen_text = CodeGenerator::new()
.with_options(CodegenOptions {
minify: minifier_options.whitespace.unwrap_or_default(),
..CodegenOptions::default()
})
.build(program)
.build(&program)
.source_text;

Ok(())
}

fn get_scope_text<'a>(program: &Program<'a>, semantic: &Semantic<'a>) -> String {
struct ScopesTextWriter<'a, 's> {
semantic: &'s Semantic<'a>,
fn run_linter(
&mut self,
run_options: &OxcRunOptions,
source_text: &str,
source_type: SourceType,
path: &Path,
trivias: &Trivias,
program: &Program,
) {
// Only lint if there are no syntax errors
if run_options.lint.unwrap_or_default() && self.diagnostics.borrow().is_empty() {
let semantic_ret = SemanticBuilder::new(source_text, source_type)
.with_cfg(true)
.with_trivias(trivias.clone())
.build_module_record(path.to_path_buf(), program)
.build(program);
let semantic = Rc::new(semantic_ret.semantic);
let linter_ret = Linter::default().run(path, Rc::clone(&semantic));
let diagnostics = linter_ret.into_iter().map(|e| Error::from(e.error)).collect();
self.save_diagnostics(diagnostics);
}
}

fn run_prettier(
&mut self,
run_options: &OxcRunOptions,
source_text: &str,
source_type: SourceType,
) {
let allocator = Allocator::default();
if run_options.prettier_format.unwrap_or_default()
|| run_options.prettier_ir.unwrap_or_default()
{
let ret = Parser::new(&allocator, source_text, source_type)
.with_options(ParseOptions { preserve_parens: false, ..ParseOptions::default() })
.parse();

let mut prettier = Prettier::new(
&allocator,
source_text,
ret.trivias.clone(),
PrettierOptions::default(),
);

if run_options.prettier_format.unwrap_or_default() {
self.prettier_formatted_text = prettier.build(&ret.program);
}

if run_options.prettier_ir.unwrap_or_default() {
let prettier_doc = prettier.doc(&ret.program).to_string();
self.prettier_ir_text = {
let ret = Parser::new(&allocator, &prettier_doc, SourceType::default()).parse();
Prettier::new(
&allocator,
&prettier_doc,
ret.trivias,
PrettierOptions::default(),
)
.build(&ret.program)
};
}
}
}

fn get_scope_text(program: &Program<'_>, symbols: &SymbolTable, scopes: &ScopeTree) -> String {
struct ScopesTextWriter<'s> {
symbols: &'s SymbolTable,
scopes: &'s ScopeTree,
scope_text: String,
indent: usize,
space: String,
}

impl<'a, 's> ScopesTextWriter<'a, 's> {
fn new(semantic: &'s Semantic<'a>) -> Self {
Self { semantic, scope_text: String::new(), indent: 0, space: String::new() }
impl<'s> ScopesTextWriter<'s> {
fn new(symbols: &'s SymbolTable, scopes: &'s ScopeTree) -> Self {
Self { symbols, scopes, scope_text: String::new(), indent: 0, space: String::new() }
}

fn write_line<S: AsRef<str>>(&mut self, line: S) {
Expand All @@ -348,17 +367,17 @@ impl Oxc {
}
}

impl<'a, 's> Visit<'a> for ScopesTextWriter<'a, 's> {
impl<'a, 's> Visit<'a> for ScopesTextWriter<'s> {
fn enter_scope(&mut self, flags: ScopeFlags, scope_id: &Cell<Option<ScopeId>>) {
let scope_id = scope_id.get().unwrap();
self.write_line(format!("Scope {} ({flags:?}) {{", scope_id.index()));
self.indent_in();

let bindings = self.semantic.scopes().get_bindings(scope_id);
let bindings = self.scopes.get_bindings(scope_id);
if !bindings.is_empty() {
self.write_line("Bindings: {");
bindings.iter().for_each(|(name, &symbol_id)| {
let symbol_flags = self.semantic.symbols().get_flags(symbol_id);
let symbol_flags = self.symbols.get_flags(symbol_id);
self.write_line(format!(" {name} ({symbol_id:?} {symbol_flags:?})",));
});
self.write_line("}");
Expand All @@ -371,7 +390,7 @@ impl Oxc {
}
}

let mut writer = ScopesTextWriter::new(semantic);
let mut writer = ScopesTextWriter::new(symbols, scopes);
writer.visit_program(program);
writer.scope_text
}
Expand Down
18 changes: 9 additions & 9 deletions crates/oxc_wasm/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ pub struct OxcOptions {
#[tsify(optional)]
pub linter: Option<OxcLinterOptions>,
#[tsify(optional)]
pub transformer: Option<OxcTransformerOptions>,
#[tsify(optional)]
pub codegen: Option<OxcCodegenOptions>,
#[tsify(optional)]
pub minifier: Option<OxcMinifierOptions>,
#[tsify(optional)]
pub type_checking: Option<OxcTypeCheckingOptions>,
}

#[derive(Debug, Default, Clone, Deserialize, Tsify)]
Expand Down Expand Up @@ -65,6 +65,13 @@ pub struct OxcParserOptions {
#[allow(clippy::empty_structs_with_brackets)]
pub struct OxcLinterOptions {}

#[derive(Debug, Default, Clone, Deserialize, Tsify)]
#[tsify(from_wasm_abi)]
#[serde(rename_all = "camelCase")]
// allow empty object for future compatibility
#[allow(clippy::empty_structs_with_brackets)]
pub struct OxcTransformerOptions {}

#[derive(Debug, Default, Clone, Deserialize, Tsify)]
#[tsify(from_wasm_abi)]
#[serde(rename_all = "camelCase")]
Expand Down Expand Up @@ -115,10 +122,3 @@ impl Default for OxcCompressOptions {
}
}
}

#[derive(Debug, Default, Clone, Deserialize, Tsify)]
#[tsify(from_wasm_abi)]
#[serde(rename_all = "camelCase")]
// allow empty object for future compatibility
#[allow(clippy::empty_structs_with_brackets)]
pub struct OxcTypeCheckingOptions {}

0 comments on commit 7fa2fa3

Please sign in to comment.