refactor(formatter,linter,codegen): remove oxc_formatter (#1968)

closes #1941
This commit is contained in:
Boshen 2024-01-10 14:41:20 +08:00 committed by GitHub
parent b5f4f1eb68
commit a6717db423
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 73 additions and 2345 deletions

1
.github/codecov.yml vendored
View file

@ -24,6 +24,5 @@ ignore:
- "crates/oxc_type_synthesis" - "crates/oxc_type_synthesis"
- "crates/oxc_query" # Not aiming for test coverage right now with @u9g - "crates/oxc_query" # Not aiming for test coverage right now with @u9g
- "crates/oxc_linter_plugin" - "crates/oxc_linter_plugin"
- "crates/oxc_formatter" # The formatter is not being actively worked on
- "crates/oxc_transformer" # not ready - "crates/oxc_transformer" # not ready
- "crates/oxc_js_regex" # not ready - "crates/oxc_js_regex" # not ready

View file

@ -12,7 +12,6 @@ on:
- 'website/**' - 'website/**'
- 'crates/oxc/**' - 'crates/oxc/**'
- 'crates/oxc_cli/**' - 'crates/oxc_cli/**'
- 'crates/oxc_formatter/**'
- 'crates/oxc_linter/**' - 'crates/oxc_linter/**'
- 'crates/oxc_query/**' - 'crates/oxc_query/**'
- 'crates/oxc_type_synthesis/**' - 'crates/oxc_type_synthesis/**'
@ -30,7 +29,6 @@ on:
- 'website/**' - 'website/**'
- 'crates/oxc/**' - 'crates/oxc/**'
- 'crates/oxc_cli/**' - 'crates/oxc_cli/**'
- 'crates/oxc_formatter/**'
- 'crates/oxc_linter/**' - 'crates/oxc_linter/**'
- 'crates/oxc_query/**' - 'crates/oxc_query/**'
- 'crates/oxc_type_synthesis/**' - 'crates/oxc_type_synthesis/**'

15
Cargo.lock generated
View file

@ -1530,7 +1530,6 @@ dependencies = [
"oxc_ast", "oxc_ast",
"oxc_codegen", "oxc_codegen",
"oxc_diagnostics", "oxc_diagnostics",
"oxc_formatter",
"oxc_index", "oxc_index",
"oxc_minifier", "oxc_minifier",
"oxc_parser", "oxc_parser",
@ -1665,17 +1664,6 @@ dependencies = [
"unicode-width", "unicode-width",
] ]
[[package]]
name = "oxc_formatter"
version = "0.4.0"
dependencies = [
"oxc_allocator",
"oxc_ast",
"oxc_parser",
"oxc_span",
"oxc_syntax",
]
[[package]] [[package]]
name = "oxc_index" name = "oxc_index"
version = "0.4.0" version = "0.4.0"
@ -1706,7 +1694,6 @@ dependencies = [
"oxc_allocator", "oxc_allocator",
"oxc_diagnostics", "oxc_diagnostics",
"oxc_linter", "oxc_linter",
"oxc_linter_plugin",
"oxc_parser", "oxc_parser",
"oxc_semantic", "oxc_semantic",
"oxc_span", "oxc_span",
@ -1734,8 +1721,8 @@ dependencies = [
"once_cell", "once_cell",
"oxc_allocator", "oxc_allocator",
"oxc_ast", "oxc_ast",
"oxc_codegen",
"oxc_diagnostics", "oxc_diagnostics",
"oxc_formatter",
"oxc_index", "oxc_index",
"oxc_macros", "oxc_macros",
"oxc_parser", "oxc_parser",

View file

@ -66,7 +66,6 @@ oxc_allocator = { version = "0.4.0", path = "crates/oxc_allocator" }
oxc_ast = { version = "0.4.0", path = "crates/oxc_ast" } oxc_ast = { version = "0.4.0", path = "crates/oxc_ast" }
oxc_codegen = { version = "0.4.0", path = "crates/oxc_codegen" } oxc_codegen = { version = "0.4.0", path = "crates/oxc_codegen" }
oxc_diagnostics = { version = "0.4.0", path = "crates/oxc_diagnostics" } oxc_diagnostics = { version = "0.4.0", path = "crates/oxc_diagnostics" }
oxc_formatter = { version = "0.4.0", path = "crates/oxc_formatter" }
oxc_index = { version = "0.4.0", path = "crates/oxc_index" } oxc_index = { version = "0.4.0", path = "crates/oxc_index" }
oxc_minifier = { version = "0.4.0", path = "crates/oxc_minifier" } oxc_minifier = { version = "0.4.0", path = "crates/oxc_minifier" }
oxc_parser = { version = "0.4.0", path = "crates/oxc_parser" } oxc_parser = { version = "0.4.0", path = "crates/oxc_parser" }

View file

@ -34,7 +34,6 @@ sed -i '' 's/0.3.0/0.4.0/' crates/oxc_allocator/Cargo.toml
sed -i '' 's/0.3.0/0.4.0/' crates/oxc_ast/Cargo.toml sed -i '' 's/0.3.0/0.4.0/' crates/oxc_ast/Cargo.toml
sed -i '' 's/0.3.0/0.4.0/' crates/oxc_codegen/Cargo.toml sed -i '' 's/0.3.0/0.4.0/' crates/oxc_codegen/Cargo.toml
sed -i '' 's/0.3.0/0.4.0/' crates/oxc_diagnostics/Cargo.toml sed -i '' 's/0.3.0/0.4.0/' crates/oxc_diagnostics/Cargo.toml
sed -i '' 's/0.3.0/0.4.0/' crates/oxc_formatter/Cargo.toml
sed -i '' 's/0.3.0/0.4.0/' crates/oxc_index/Cargo.toml sed -i '' 's/0.3.0/0.4.0/' crates/oxc_index/Cargo.toml
sed -i '' 's/0.3.0/0.4.0/' crates/oxc_minifier/Cargo.toml sed -i '' 's/0.3.0/0.4.0/' crates/oxc_minifier/Cargo.toml
sed -i '' 's/0.3.0/0.4.0/' crates/oxc_parser/Cargo.toml sed -i '' 's/0.3.0/0.4.0/' crates/oxc_parser/Cargo.toml
@ -60,7 +59,6 @@ cargo publish -p oxc_ast
cargo publish -p oxc_diagnostics cargo publish -p oxc_diagnostics
cargo publish -p oxc_parser cargo publish -p oxc_parser
cargo publish -p oxc_semantic cargo publish -p oxc_semantic
cargo publish -p oxc_formatter
cargo publish -p oxc_transformer cargo publish -p oxc_transformer
cargo publish -p oxc_codegen cargo publish -p oxc_codegen
cargo publish -p oxc_minifier cargo publish -p oxc_minifier

View file

@ -28,7 +28,6 @@ oxc_parser = { workspace = true }
oxc_span = { workspace = true } oxc_span = { workspace = true }
oxc_syntax = { workspace = true } oxc_syntax = { workspace = true }
oxc_semantic = { workspace = true, optional = true } oxc_semantic = { workspace = true, optional = true }
oxc_formatter = { workspace = true, optional = true }
oxc_transformer = { workspace = true, optional = true } oxc_transformer = { workspace = true, optional = true }
oxc_minifier = { workspace = true, optional = true } oxc_minifier = { workspace = true, optional = true }
oxc_codegen = { workspace = true, optional = true } oxc_codegen = { workspace = true, optional = true }
@ -36,7 +35,6 @@ oxc_codegen = { workspace = true, optional = true }
[features] [features]
serde = ["oxc_ast/serde", "oxc_semantic/serde"] serde = ["oxc_ast/serde", "oxc_semantic/serde"]
semantic = ["oxc_semantic"] semantic = ["oxc_semantic"]
formatter = ["oxc_formatter"]
transformer = ["oxc_transformer"] transformer = ["oxc_transformer"]
minifier = ["oxc_minifier"] minifier = ["oxc_minifier"]
codegen = ["oxc_codegen"] codegen = ["oxc_codegen"]

View file

@ -43,12 +43,6 @@ pub mod semantic {
pub use oxc_semantic::*; pub use oxc_semantic::*;
} }
#[cfg(feature = "formatter")]
pub mod formatter {
#[doc(inline)]
pub use oxc_formatter::*;
}
#[cfg(feature = "transformer")] #[cfg(feature = "transformer")]
pub mod transformer { pub mod transformer {
#[doc(inline)] #[doc(inline)]

View file

@ -21,6 +21,7 @@ impl Context {
} }
#[inline] #[inline]
#[must_use]
pub fn and_in(self, include: bool) -> Self { pub fn and_in(self, include: bool) -> Self {
self.and(Self::In, include) self.and(Self::In, include)
} }

View file

@ -129,7 +129,7 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for ExpressionStatement<'a> {
fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) {
p.print_indent(); p.print_indent();
p.start_of_stmt = p.code_len(); p.start_of_stmt = p.code_len();
self.expression.gen_expr(p, Precedence::lowest(), Context::default()); p.print_expression(&self.expression);
if self.expression.is_specific_id("let") { if self.expression.is_specific_id("let") {
p.print_semicolon(); p.print_semicolon();
} else { } else {
@ -153,7 +153,7 @@ fn print_if<const MINIFY: bool>(
p.print_str(b"if"); p.print_str(b"if");
p.print_soft_space(); p.print_soft_space();
p.print(b'('); p.print(b'(');
if_stmt.test.gen_expr(p, Precedence::lowest(), Context::default()); p.print_expression(&if_stmt.test);
p.print(b')'); p.print(b')');
p.print_soft_space(); p.print_soft_space();
@ -251,14 +251,14 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for ForStatement<'a> {
p.print_soft_space(); p.print_soft_space();
if let Some(test) = self.test.as_ref() { if let Some(test) = self.test.as_ref() {
test.gen_expr(p, Precedence::lowest(), Context::default()); p.print_expression(test);
} }
p.print_semicolon(); p.print_semicolon();
p.print_soft_space(); p.print_soft_space();
if let Some(update) = self.update.as_ref() { if let Some(update) = self.update.as_ref() {
update.gen_expr(p, Precedence::lowest(), Context::default()); p.print_expression(update);
} }
p.print(b')'); p.print(b')');
@ -277,7 +277,7 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for ForInStatement<'a> {
p.print_space_before_identifier(); p.print_space_before_identifier();
p.print_str(b"in"); p.print_str(b"in");
p.print_hard_space(); p.print_hard_space();
self.right.gen_expr(p, Precedence::lowest(), Context::default()); p.print_expression(&self.right);
p.print(b')'); p.print(b')');
p.print_soft_space(); p.print_soft_space();
self.body.gen(p, ctx); self.body.gen(p, ctx);
@ -320,7 +320,7 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for WhileStatement<'a> {
p.print_indent(); p.print_indent();
p.print_str(b"while"); p.print_str(b"while");
p.print(b'('); p.print(b'(');
self.test.gen_expr(p, Precedence::lowest(), Context::default()); p.print_expression(&self.test);
p.print(b')'); p.print(b')');
self.body.gen(p, ctx); self.body.gen(p, ctx);
} }
@ -342,7 +342,7 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for DoWhileStatement<'a> {
} }
p.print_str(b"while"); p.print_str(b"while");
p.print(b'('); p.print(b'(');
self.test.gen_expr(p, Precedence::lowest(), Context::default()); p.print_expression(&self.test);
p.print(b')'); p.print(b')');
p.print_semicolon_after_statement(); p.print_semicolon_after_statement();
} }
@ -384,7 +384,7 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for SwitchStatement<'a> {
p.print_indent(); p.print_indent();
p.print_str(b"switch"); p.print_str(b"switch");
p.print(b'('); p.print(b'(');
self.discriminant.gen_expr(p, Precedence::lowest(), Context::default()); p.print_expression(&self.discriminant);
p.print(b')'); p.print(b')');
p.print_block_start(); p.print_block_start();
for case in &self.cases { for case in &self.cases {
@ -404,7 +404,7 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for SwitchCase<'a> {
Some(test) => { Some(test) => {
p.print_str(b"case"); p.print_str(b"case");
p.print_hard_space(); p.print_hard_space();
test.gen_expr(p, Precedence::lowest(), Context::default()); p.print_expression(test);
} }
None => p.print_str(b"default"), None => p.print_str(b"default"),
} }
@ -425,7 +425,7 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for ReturnStatement<'a> {
p.print_str(b"return"); p.print_str(b"return");
if let Some(arg) = &self.argument { if let Some(arg) = &self.argument {
p.print_hard_space(); p.print_hard_space();
arg.gen_expr(p, Precedence::lowest(), Context::default()); p.print_expression(arg);
} }
p.print_semicolon_after_statement(); p.print_semicolon_after_statement();
} }
@ -465,7 +465,7 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for ThrowStatement<'a> {
fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) {
p.print_indent(); p.print_indent();
p.print_str(b"throw "); p.print_str(b"throw ");
self.argument.gen_expr(p, Precedence::lowest(), Context::default()); p.print_expression(&self.argument);
p.print_semicolon_after_statement(); p.print_semicolon_after_statement();
} }
} }
@ -475,7 +475,7 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for WithStatement<'a> {
p.print_indent(); p.print_indent();
p.print_str(b"with"); p.print_str(b"with");
p.print(b'('); p.print(b'(');
self.object.gen_expr(p, Precedence::lowest(), Context::default()); p.print_expression(&self.object);
p.print(b')'); p.print(b')');
self.body.gen(p, ctx); self.body.gen(p, ctx);
} }
@ -1645,7 +1645,7 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for TemplateLiteral<'a> {
if let Some(expr) = expressions.next() { if let Some(expr) = expressions.next() {
p.print_str(b"${"); p.print_str(b"${");
expr.gen_expr(p, Precedence::lowest(), Context::default()); p.print_expression(expr);
p.print(b'}'); p.print(b'}');
} }
} }
@ -1825,7 +1825,7 @@ impl<const MINIFY: bool> Gen<MINIFY> for JSXEmptyExpression {
impl<'a, const MINIFY: bool> Gen<MINIFY> for JSXExpression<'a> { impl<'a, const MINIFY: bool> Gen<MINIFY> for JSXExpression<'a> {
fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) {
match self { match self {
Self::Expression(expr) => expr.gen_expr(p, Precedence::lowest(), Context::default()), Self::Expression(expr) => p.print_expression(expr),
Self::EmptyExpression(expr) => expr.gen(p, ctx), Self::EmptyExpression(expr) => expr.gen(p, ctx),
} }
} }
@ -1853,7 +1853,7 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for JSXAttributeValue<'a> {
impl<'a, const MINIFY: bool> Gen<MINIFY> for JSXSpreadAttribute<'a> { impl<'a, const MINIFY: bool> Gen<MINIFY> for JSXSpreadAttribute<'a> {
fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) {
p.print_str(b"{..."); p.print_str(b"{...");
self.argument.gen_expr(p, Precedence::lowest(), Context::default()); p.print_expression(&self.argument);
p.print(b'}'); p.print(b'}');
} }
} }
@ -1924,7 +1924,7 @@ impl<const MINIFY: bool> Gen<MINIFY> for JSXText {
impl<'a, const MINIFY: bool> Gen<MINIFY> for JSXSpreadChild<'a> { impl<'a, const MINIFY: bool> Gen<MINIFY> for JSXSpreadChild<'a> {
fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) {
p.print_str(b"..."); p.print_str(b"...");
self.expression.gen_expr(p, Precedence::lowest(), Context::default()); p.print_expression(&self.expression);
} }
} }
@ -1933,9 +1933,7 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for JSXChild<'a> {
match self { match self {
Self::Fragment(fragment) => fragment.gen(p, ctx), Self::Fragment(fragment) => fragment.gen(p, ctx),
Self::Element(el) => el.gen(p, ctx), Self::Element(el) => el.gen(p, ctx),
Self::Spread(spread) => { Self::Spread(spread) => p.print_expression(&spread.expression),
spread.expression.gen_expr(p, Precedence::lowest(), Context::default());
}
Self::ExpressionContainer(expr_container) => expr_container.gen(p, ctx), Self::ExpressionContainer(expr_container) => expr_container.gen(p, ctx),
Self::Text(text) => text.gen(p, ctx), Self::Text(text) => text.gen(p, ctx),
} }

View file

@ -24,7 +24,7 @@ use oxc_syntax::{
symbol::SymbolId, symbol::SymbolId,
}; };
use self::{ pub use crate::{
context::Context, context::Context,
gen::{Gen, GenExpr}, gen::{Gen, GenExpr},
operator::Operator, operator::Operator,
@ -111,12 +111,12 @@ impl<const MINIFY: bool> Codegen<MINIFY> {
} }
/// Push a single character into the buffer /// Push a single character into the buffer
fn print(&mut self, ch: u8) { pub fn print(&mut self, ch: u8) {
self.code.push(ch); self.code.push(ch);
} }
/// Push a string into the buffer /// Push a string into the buffer
fn print_str(&mut self, s: &[u8]) { pub fn print_str(&mut self, s: &[u8]) {
self.code.extend_from_slice(s); self.code.extend_from_slice(s);
} }
@ -126,7 +126,7 @@ impl<const MINIFY: bool> Codegen<MINIFY> {
} }
} }
fn print_hard_space(&mut self) { pub fn print_hard_space(&mut self) {
self.print(b' '); self.print(b' ');
} }
@ -197,7 +197,7 @@ impl<const MINIFY: bool> Codegen<MINIFY> {
self.print_str(b"..."); self.print_str(b"...");
} }
fn print_colon(&mut self) { pub fn print_colon(&mut self) {
self.print(b':'); self.print(b':');
} }
@ -256,6 +256,10 @@ impl<const MINIFY: bool> Codegen<MINIFY> {
} }
} }
pub fn print_expression(&mut self, expr: &Expression<'_>) {
expr.gen_expr(self, Precedence::lowest(), Context::default());
}
fn print_expressions<T: GenExpr<MINIFY>>( fn print_expressions<T: GenExpr<MINIFY>>(
&mut self, &mut self,
items: &[T], items: &[T],

View file

@ -1,27 +0,0 @@
[package]
name = "oxc_formatter"
version = "0.4.0"
authors.workspace = true
description.workspace = true
edition.workspace = true
homepage.workspace = true
keywords.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
categories.workspace = true
[lints]
workspace = true
[lib]
doctest = false
[dependencies]
oxc_allocator = { workspace = true }
oxc_ast = { workspace = true }
oxc_span = { workspace = true }
oxc_syntax = { workspace = true }
[dev-dependencies]
oxc_parser = { workspace = true }

View file

@ -1,3 +0,0 @@
# Formatter (Prettier)
TBD

View file

@ -1,32 +0,0 @@
use std::{env, path::Path};
use oxc_allocator::Allocator;
use oxc_formatter::{Formatter, FormatterOptions};
use oxc_parser::Parser;
use oxc_span::SourceType;
// Instruction:
// create a `test.js`,
// run `cargo run -p oxc_formatter --example formatter`
// or `cargo watch -x "run -p oxc_formatter --example formatter"`
fn main() {
let name = env::args().nth(1).unwrap_or_else(|| "test.js".to_string());
let path = Path::new(&name);
let source_text = std::fs::read_to_string(path).expect("{name} not found");
let allocator = Allocator::default();
let source_type = SourceType::from_path(path).unwrap();
let ret = Parser::new(&allocator, &source_text, source_type).parse();
if !ret.errors.is_empty() {
for error in ret.errors {
let error = error.with_source_code(source_text.clone());
println!("{error:?}");
}
return;
}
let formatter_options = FormatterOptions::default();
let printed = Formatter::new(source_text.len(), formatter_options).build(&ret.program);
println!("{printed}");
}

File diff suppressed because it is too large Load diff

View file

@ -1,323 +0,0 @@
//! Prettier
//!
//! This crate is intended to be [prettier](https://prettier.io).
//! Please use the `oxc_codegen ` for code generation.
mod gen;
#[allow(clippy::wildcard_imports)]
use oxc_ast::ast::*;
pub use crate::gen::Gen;
#[derive(Debug, PartialEq, Eq, Clone)]
/// @see [prettier](https://prettier.io/docs/en/options.html#end-of-line)
pub enum EndOfLine {
/// Line Feed only (`\n`), common on Linux and macOS as well as inside git repos
LF,
/// Carriage Return + Line Feed characters (`\r\n`), common on Windows
CRLF,
/// Carriage Return character only (`\r`), used very rarely
CR,
/// Maintain existing line endings (mixed values within one file are normalised by looking at whats used after the first line)
Auto(String),
}
impl EndOfLine {
pub fn get_final_end_of_line(&self) -> FinalEndOfLine {
match self {
Self::Auto(raw_input) => Self::auto_detect_end_of_line(raw_input),
Self::LF => FinalEndOfLine::LF,
Self::CRLF => FinalEndOfLine::CRLF,
Self::CR => FinalEndOfLine::CR,
}
}
pub fn auto_detect_end_of_line(raw_input_text: &str) -> FinalEndOfLine {
let first_line_feed_pos = raw_input_text.chars().position(|ch| ch == '\n');
first_line_feed_pos.map_or(FinalEndOfLine::CR, |first_line_feed_pos| {
let char_before_line_feed_pos = first_line_feed_pos.saturating_sub(1);
let char_before_line_feed = raw_input_text.chars().nth(char_before_line_feed_pos);
match char_before_line_feed {
Some('\r') => FinalEndOfLine::CRLF,
_ => FinalEndOfLine::LF,
}
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FinalEndOfLine {
LF,
CRLF,
CR,
}
#[derive(Debug, Clone)]
pub struct FormatterOptions {
pub indentation: u8,
// <https://prettier.io/docs/en/options#quotes>
pub single_quote: bool,
pub end_of_line: EndOfLine,
}
impl Default for FormatterOptions {
fn default() -> Self {
Self { indentation: 4, single_quote: false, end_of_line: EndOfLine::LF }
}
}
#[derive(Debug)]
/// processed and reserved for internal use
pub struct InnerOptions {
pub indentation: u8,
pub end_of_line: FinalEndOfLine,
pub single_quote: bool,
}
impl From<FormatterOptions> for InnerOptions {
fn from(options: FormatterOptions) -> Self {
Self {
indentation: options.indentation,
single_quote: options.single_quote,
end_of_line: options.end_of_line.get_final_end_of_line(),
}
}
}
pub struct Formatter {
options: InnerOptions,
/// Output Code
code: Vec<u8>,
/// Current indentation tracking
indentation: u8,
// states
needs_semicolon: bool,
// Quote property with double quotes
quote_property_with_double_quotes: bool,
}
#[derive(Debug, Clone, Copy)]
pub enum Separator {
Comma,
Semicolon,
None,
}
/// Codegen interface for pretty print or minification
impl Formatter {
pub fn new(source_len: usize, options: FormatterOptions) -> Self {
Self {
options: options.into(),
code: Vec::with_capacity(source_len),
indentation: 0,
needs_semicolon: false,
quote_property_with_double_quotes: false,
}
}
pub fn build(mut self, program: &Program<'_>) -> String {
program.gen(&mut self);
self.into_code()
}
#[inline]
pub fn into_code(self) -> String {
// SAFETY: criteria of `from_utf8_unchecked`.are met.
unsafe { String::from_utf8_unchecked(self.code) }
}
pub fn code(&self) -> &Vec<u8> {
&self.code
}
/// Push a single character into the buffer
#[inline]
pub fn print(&mut self, ch: u8) {
self.code.push(ch);
}
/// Push a string into the buffer
#[inline]
pub fn print_str(&mut self, s: &[u8]) {
self.code.extend_from_slice(s);
}
#[inline]
pub fn print_space(&mut self) {
self.code.push(b' ');
}
#[inline]
pub fn print_newline(&mut self) {
match self.options.end_of_line {
FinalEndOfLine::LF => {
self.code.push(b'\n');
}
FinalEndOfLine::CRLF => {
self.code.push(b'\r');
self.code.push(b'\n');
}
FinalEndOfLine::CR => {
self.code.push(b'\r');
}
}
}
#[inline]
pub fn indent(&mut self) {
self.indentation += self.options.indentation;
}
#[inline]
pub fn dedent(&mut self) {
self.indentation -= self.options.indentation;
}
#[inline]
pub fn print_semicolon(&mut self) {
self.print(b';');
}
#[inline]
pub fn print_comma(&mut self) {
self.print(b',');
}
fn print_semicolon_after_statement(&mut self) {
self.print_semicolon();
self.print_newline();
}
fn print_semicolon_if_needed(&mut self) {
if self.needs_semicolon {
self.print_semicolon();
self.needs_semicolon = false;
}
}
#[inline]
pub fn print_ellipsis(&mut self) {
self.print_str(b"...");
}
#[inline]
pub fn print_colon(&mut self) {
self.print(b':');
}
#[inline]
pub fn print_equal(&mut self) {
self.print(b'=');
}
#[inline]
pub fn print_quote(&mut self) {
self.print(if self.options.single_quote { b'\'' } else { b'"' });
}
pub fn print_indent(&mut self) {
for _ in 0..self.indentation {
self.print(b' ');
}
}
#[inline]
pub fn print_sequence<T: Gen>(&mut self, items: &[T], separator: Separator) {
let len = items.len();
for (index, item) in items.iter().enumerate() {
item.gen(self);
match separator {
Separator::Semicolon => self.print_semicolon(),
Separator::Comma => self.print(b','),
Separator::None => {}
}
if index != len - 1 {
self.print_newline();
}
}
}
#[inline]
pub fn print_body(&mut self, stmt: &Statement<'_>) {
if let Statement::BlockStatement(block) = stmt {
self.print_space();
self.print_block1(block);
self.print_newline();
} else {
self.print_newline();
self.indent();
stmt.gen(self);
self.dedent();
}
}
#[inline]
pub fn print_block1(&mut self, stmt: &BlockStatement<'_>) {
self.print(b'{');
self.print_newline();
self.indent();
for item in &stmt.body {
self.print_semicolon_if_needed();
item.gen(self);
}
self.dedent();
self.needs_semicolon = false;
self.print_indent();
self.print(b'}');
}
#[inline]
pub fn print_block<T: Gen>(&mut self, items: &[T], separator: Separator) {
self.print(b'{');
self.indent();
if !items.is_empty() {
self.print_newline();
}
self.print_sequence(items, separator);
self.dedent();
if !items.is_empty() {
self.print_newline();
}
self.print(b'}');
}
#[inline]
pub fn print_list<T: Gen>(&mut self, items: &[T]) {
for (index, item) in items.iter().enumerate() {
if index != 0 {
self.print_comma();
self.print_space();
}
item.gen(self);
}
}
pub fn last_char(&self) -> Option<&u8> {
self.code.last()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn auto_detects_lf() {
assert_eq!(FinalEndOfLine::LF, EndOfLine::auto_detect_end_of_line("One\nTwo\nThree"));
}
#[test]
fn auto_detects_crlf() {
assert_eq!(FinalEndOfLine::CRLF, EndOfLine::auto_detect_end_of_line("One\r\nTwo\r\nThree"));
}
#[test]
fn auto_detects_cr() {
assert_eq!(FinalEndOfLine::CR, EndOfLine::auto_detect_end_of_line("One\rTwo\rThree"));
}
}

View file

@ -26,7 +26,6 @@ oxc_linter = { workspace = true }
oxc_parser = { workspace = true } oxc_parser = { workspace = true }
oxc_semantic = { workspace = true } oxc_semantic = { workspace = true }
oxc_span = { workspace = true } oxc_span = { workspace = true }
oxc_linter_plugin = { workspace = true }
dashmap = { workspace = true } dashmap = { workspace = true }
env_logger = { workspace = true } env_logger = { workspace = true }
futures = { workspace = true } futures = { workspace = true }

View file

@ -27,7 +27,7 @@ oxc_diagnostics = { workspace = true }
oxc_macros = { workspace = true } oxc_macros = { workspace = true }
oxc_semantic = { workspace = true } oxc_semantic = { workspace = true }
oxc_syntax = { workspace = true } oxc_syntax = { workspace = true }
oxc_formatter = { workspace = true } oxc_codegen = { workspace = true }
oxc_index = { workspace = true } oxc_index = { workspace = true }
oxc_resolver = { version = "1.2.0" } oxc_resolver = { version = "1.2.0" }

View file

@ -1,7 +1,7 @@
use std::{cell::RefCell, path::Path, rc::Rc}; use std::{cell::RefCell, path::Path, rc::Rc};
use oxc_codegen::{Codegen, CodegenOptions};
use oxc_diagnostics::Error; use oxc_diagnostics::Error;
use oxc_formatter::{Formatter, FormatterOptions};
use oxc_semantic::{AstNodes, JSDocComment, ScopeTree, Semantic, SymbolTable}; use oxc_semantic::{AstNodes, JSDocComment, ScopeTree, Semantic, SymbolTable};
use oxc_span::SourceType; use oxc_span::SourceType;
@ -119,8 +119,8 @@ impl<'a> LintContext<'a> {
} }
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
pub fn formatter(&self) -> Formatter { pub fn codegen(&self) -> Codegen<false> {
Formatter::new(0, FormatterOptions::default()) Codegen::<false>::new(0, CodegenOptions)
} }
/* JSDoc */ /* JSDoc */

View file

@ -6,7 +6,6 @@ use oxc_diagnostics::{
miette::{self, Diagnostic}, miette::{self, Diagnostic},
thiserror::{self, Error}, thiserror::{self, Error},
}; };
use oxc_formatter::Gen;
use oxc_macros::declare_oxc_lint; use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span}; use oxc_span::{GetSpan, Span};
use oxc_syntax::operator::{BinaryOperator, UnaryOperator}; use oxc_syntax::operator::{BinaryOperator, UnaryOperator};
@ -83,21 +82,22 @@ impl NoUnsafeNegation {
/// Precondition: /// Precondition:
/// expr.left is `UnaryExpression` whose operator is '!' /// expr.left is `UnaryExpression` whose operator is '!'
fn report_with_fix(expr: &BinaryExpression, ctx: &LintContext<'_>) { fn report_with_fix(expr: &BinaryExpression, ctx: &LintContext<'_>) {
use oxc_codegen::{Context, Gen};
// Diagnostic points at the unexpected negation // Diagnostic points at the unexpected negation
let diagnostic = NoUnsafeNegationDiagnostic(expr.operator.as_str(), expr.left.span()); let diagnostic = NoUnsafeNegationDiagnostic(expr.operator.as_str(), expr.left.span());
let fix_producer = || { let fix_producer = || {
// modify `!a instance of B` to `!(a instanceof B)` // modify `!a instance of B` to `!(a instanceof B)`
let modified_code = { let modified_code = {
let mut formatter = ctx.formatter(); let mut codegen = ctx.codegen();
formatter.print(b'!'); codegen.print(b'!');
let Expression::UnaryExpression(left) = &expr.left else { unreachable!() }; let Expression::UnaryExpression(left) = &expr.left else { unreachable!() };
formatter.print(b'('); codegen.print(b'(');
left.argument.gen(&mut formatter); codegen.print_expression(&left.argument);
expr.operator.gen(&mut formatter); expr.operator.gen(&mut codegen, Context::default());
expr.right.gen(&mut formatter); codegen.print_expression(&expr.right);
formatter.print(b')'); codegen.print(b')');
formatter.into_code() codegen.into_code()
}; };
Fix::new(modified_code, expr.span) Fix::new(modified_code, expr.span)
}; };

View file

@ -147,7 +147,7 @@ fn get_fix_content<'a>(expr: &'a CallExpression<'a>) -> (&'a str, Span) {
} }
fn build_code(expr: &CallExpression, ctx: &LintContext) -> (String, Span) { fn build_code(expr: &CallExpression, ctx: &LintContext) -> (String, Span) {
let mut formatter = ctx.formatter(); let mut formatter = ctx.codegen();
if let Expression::Identifier(ident) = &expr.callee { if let Expression::Identifier(ident) = &expr.callee {
formatter.print_str(ident.name.as_bytes()); formatter.print_str(ident.name.as_bytes());

View file

@ -4,9 +4,8 @@ use oxc_diagnostics::{
miette::{self, Diagnostic}, miette::{self, Diagnostic},
thiserror::Error, thiserror::Error,
}; };
use oxc_formatter::Gen;
use oxc_macros::declare_oxc_lint; use oxc_macros::declare_oxc_lint;
use oxc_span::Span; use oxc_span::{GetSpan, Span};
use oxc_syntax::operator::BinaryOperator; use oxc_syntax::operator::BinaryOperator;
use crate::{context::LintContext, fixer::Fix, rule::Rule, AstNode}; use crate::{context::LintContext, fixer::Fix, rule::Rule, AstNode};
@ -46,11 +45,11 @@ impl Rule for NoInstanceofArray {
Expression::Identifier(identifier) if identifier.name == "Array" => { Expression::Identifier(identifier) if identifier.name == "Array" => {
ctx.diagnostic_with_fix(NoInstanceofArrayDiagnostic(expr.span), || { ctx.diagnostic_with_fix(NoInstanceofArrayDiagnostic(expr.span), || {
let modified_code = { let modified_code = {
let mut formatter = ctx.formatter(); let mut codegen = String::new();
formatter.print_str(b"Array.isArray("); codegen.push_str("Array.isArray(");
expr.left.gen(&mut formatter); codegen.push_str(expr.left.span().source_text(ctx.source_text()));
formatter.print(b')'); codegen.push(')');
formatter.into_code() codegen
}; };
Fix::new(modified_code, expr.span) Fix::new(modified_code, expr.span)
}); });
@ -90,7 +89,7 @@ fn test() {
let fix = vec![ let fix = vec![
("arr instanceof Array", "Array.isArray(arr)", None), ("arr instanceof Array", "Array.isArray(arr)", None),
("[] instanceof Array", "Array.isArray([])", None), ("[] instanceof Array", "Array.isArray([])", None),
("[1,2,3] instanceof Array === true", "Array.isArray([1, 2, 3]) === true", None), ("[1,2,3] instanceof Array === true", "Array.isArray([1,2,3]) === true", None),
("fun.call(1, 2, 3) instanceof Array", "Array.isArray(fun.call(1, 2, 3))", None), ("fun.call(1, 2, 3) instanceof Array", "Array.isArray(fun.call(1, 2, 3))", None),
("obj.arr instanceof Array", "Array.isArray(obj.arr)", None), ("obj.arr instanceof Array", "Array.isArray(obj.arr)", None),
("foo.bar[2] instanceof Array", "Array.isArray(foo.bar[2])", None), ("foo.bar[2] instanceof Array", "Array.isArray(foo.bar[2])", None),

View file

@ -3,9 +3,8 @@ use oxc_diagnostics::{
miette::{self, Diagnostic}, miette::{self, Diagnostic},
thiserror::Error, thiserror::Error,
}; };
use oxc_formatter::Gen;
use oxc_macros::declare_oxc_lint; use oxc_macros::declare_oxc_lint;
use oxc_span::Span; use oxc_span::{GetSpan, Span};
use crate::{context::LintContext, rule::Rule, AstNode, Fix}; use crate::{context::LintContext, rule::Rule, AstNode, Fix};
@ -64,9 +63,9 @@ impl Rule for NoUnnecessaryAwait {
ctx.diagnostic_with_fix( ctx.diagnostic_with_fix(
NoUnnecessaryAwaitDiagnostic(Span::new(expr.span.start, expr.span.start + 5)), NoUnnecessaryAwaitDiagnostic(Span::new(expr.span.start, expr.span.start + 5)),
|| { || {
let mut formatter = ctx.formatter(); let mut codegen = String::new();
expr.argument.gen(&mut formatter); codegen.push_str(expr.argument.span().source_text(ctx.source_text()));
Fix::new(formatter.into_code(), expr.span) Fix::new(codegen, expr.span)
}, },
); );
}; };
@ -158,8 +157,8 @@ fn test() {
let fix = vec![ let fix = vec![
("await []", "[]", None), ("await []", "[]", None),
("await (a == b)", "(a == b)", None), ("await (a == b)", "(a == b)", None),
("+await -1", "+ -1", None), ("+await -1", "+-1", None),
("-await +1", "- +1", None), ("-await +1", "-+1", None),
("await function() {}", "await function() {}", None), // no autofix ("await function() {}", "await function() {}", None), // no autofix
("await class {}", "await class {}", None), // no autofix ("await class {}", "await class {}", None), // no autofix
("+await +1", "+await +1", None), // no autofix ("+await +1", "+await +1", None), // no autofix

View file

@ -64,7 +64,7 @@ impl Rule for RequireNumberToFixedDigitsArgument {
RequireNumberToFixedDigitsArgumentDiagnostic(parenthesis_span), RequireNumberToFixedDigitsArgumentDiagnostic(parenthesis_span),
|| { || {
let modified_code = { let modified_code = {
let mut formatter = ctx.formatter(); let mut formatter = ctx.codegen();
let mut parenthesis_span_without_right_one = parenthesis_span; let mut parenthesis_span_without_right_one = parenthesis_span;
parenthesis_span_without_right_one.end -= 1; parenthesis_span_without_right_one.end -= 1;

View file

@ -3,7 +3,6 @@ use oxc_diagnostics::{
miette::{self, Diagnostic}, miette::{self, Diagnostic},
thiserror::Error, thiserror::Error,
}; };
use oxc_formatter::Gen;
use oxc_macros::declare_oxc_lint; use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span}; use oxc_span::{GetSpan, Span};
@ -76,20 +75,23 @@ impl Rule for SwitchCaseBraces {
}; };
ctx.diagnostic_with_fix(SwitchCaseBracesDiagnostic(case_body_span), || { ctx.diagnostic_with_fix(SwitchCaseBracesDiagnostic(case_body_span), || {
use oxc_codegen::{Context, Gen};
let modified_code = { let modified_code = {
let mut formatter = ctx.formatter(); let mut formatter = ctx.codegen();
if let Some(case_test) = &case.test { if let Some(case_test) = &case.test {
formatter.print_str(b"case "); formatter.print_str(b"case ");
case_test.gen(&mut formatter); formatter.print_expression(case_test);
} else { } else {
formatter.print_str(b"default"); formatter.print_str(b"default");
} }
formatter.print_colon(); formatter.print_colon();
formatter.print_space(); formatter.print_hard_space();
formatter.print(b'{'); formatter.print(b'{');
case.consequent.iter().for_each(|x| x.gen(&mut formatter)); case.consequent
.iter()
.for_each(|x| x.gen(&mut formatter, Context::default()));
formatter.print(b'}'); formatter.print(b'}');
formatter.into_code() formatter.into_code()
@ -140,7 +142,7 @@ fn test() {
), ),
( (
"switch(something) { case 1: {} case 2: console.log('something'); break;}", "switch(something) { case 1: {} case 2: console.log('something'); break;}",
"switch(something) { case 1: case 2: {console.log(\"something\");\nbreak;\n}}", "switch(something) { case 1: case 2: {console.log('something');\nbreak;\n}}",
None, None,
), ),
( (

View file

@ -21,7 +21,7 @@ doctest = false
default = ["console_error_panic_hook"] default = ["console_error_panic_hook"]
[dependencies] [dependencies]
oxc = { workspace = true, features = ["serde", "semantic", "formatter", "transformer", "minifier", "codegen"] } oxc = { workspace = true, features = ["serde", "semantic", "transformer", "minifier", "codegen"] }
oxc_linter = { workspace = true } oxc_linter = { workspace = true }
oxc_prettier = { workspace = true } oxc_prettier = { workspace = true }

View file

@ -6,7 +6,6 @@ use oxc::{
allocator::Allocator, allocator::Allocator,
codegen::{Codegen, CodegenOptions}, codegen::{Codegen, CodegenOptions},
diagnostics::Error, diagnostics::Error,
formatter::{Formatter, FormatterOptions},
minifier::{CompressOptions, Minifier, MinifierOptions}, minifier::{CompressOptions, Minifier, MinifierOptions},
parser::{Parser, ParserReturn}, parser::{Parser, ParserReturn},
semantic::{ScopeId, Semantic, SemanticBuilder, SemanticBuilderReturn}, semantic::{ScopeId, Semantic, SemanticBuilder, SemanticBuilderReturn},
@ -22,7 +21,7 @@ use trustfall::{execute_query, TransparentValue};
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use crate::options::{ use crate::options::{
OxcFormatterOptions, OxcLinterOptions, OxcMinifierOptions, OxcParserOptions, OxcRunOptions, OxcCodegenOptions, OxcLinterOptions, OxcMinifierOptions, OxcParserOptions, OxcRunOptions,
OxcTypeCheckingOptions, OxcTypeCheckingOptions,
}; };
@ -182,7 +181,7 @@ impl Oxc {
run_options: &OxcRunOptions, run_options: &OxcRunOptions,
parser_options: &OxcParserOptions, parser_options: &OxcParserOptions,
_linter_options: &OxcLinterOptions, _linter_options: &OxcLinterOptions,
formatter_options: &OxcFormatterOptions, _codegen_options: &OxcCodegenOptions,
minifier_options: &OxcMinifierOptions, minifier_options: &OxcMinifierOptions,
_type_checking_options: &OxcTypeCheckingOptions, _type_checking_options: &OxcTypeCheckingOptions,
) -> Result<(), serde_wasm_bindgen::Error> { ) -> Result<(), serde_wasm_bindgen::Error> {
@ -226,15 +225,6 @@ impl Oxc {
self.save_diagnostics(diagnostics); self.save_diagnostics(diagnostics);
} }
if run_options.format() {
let formatter_options = FormatterOptions {
indentation: formatter_options.indentation,
..Default::default()
};
let printed = Formatter::new(source_text.len(), formatter_options).build(program);
self.formatted_text = printed;
}
if run_options.prettier_format() { if run_options.prettier_format() {
let ret = Parser::new(&allocator, source_text, source_type) let ret = Parser::new(&allocator, source_text, source_type)
.allow_return_outside_function(parser_options.allow_return_outside_function) .allow_return_outside_function(parser_options.allow_return_outside_function)

View file

@ -141,12 +141,12 @@ impl OxcLinterOptions {
#[wasm_bindgen] #[wasm_bindgen]
#[derive(Default, Clone, Copy)] #[derive(Default, Clone, Copy)]
pub struct OxcFormatterOptions { pub struct OxcCodegenOptions {
pub indentation: u8, pub indentation: u8,
} }
#[wasm_bindgen] #[wasm_bindgen]
impl OxcFormatterOptions { impl OxcCodegenOptions {
#[wasm_bindgen(constructor)] #[wasm_bindgen(constructor)]
pub fn new() -> Self { pub fn new() -> Self {
Self::default() Self::default()

View file

@ -34,7 +34,7 @@ import initWasm, {
OxcParserOptions, OxcParserOptions,
OxcLinterOptions, OxcLinterOptions,
OxcMinifierOptions, OxcMinifierOptions,
OxcFormatterOptions, OxcCodegenOptions,
OxcTypeCheckingOptions, OxcTypeCheckingOptions,
graphql_schema_text, graphql_schema_text,
} from "@oxc/wasm-web"; } from "@oxc/wasm-web";
@ -87,7 +87,7 @@ class Playground {
runOptions; runOptions;
parserOptions; parserOptions;
formatterOptions; codegenOptions;
linterOptions; linterOptions;
minifierOptions; minifierOptions;
@ -118,7 +118,7 @@ class Playground {
this.oxc = new Oxc(); this.oxc = new Oxc();
this.runOptions = new OxcRunOptions(); this.runOptions = new OxcRunOptions();
this.parserOptions = new OxcParserOptions(); this.parserOptions = new OxcParserOptions();
this.formatterOptions = new OxcFormatterOptions(); this.codegenOptions = new OxcCodegenOptions();
this.linterOptions = new OxcLinterOptions(); this.linterOptions = new OxcLinterOptions();
this.minifierOptions = new OxcMinifierOptions(); this.minifierOptions = new OxcMinifierOptions();
this.typeCheckOptions = new OxcTypeCheckingOptions(); this.typeCheckOptions = new OxcTypeCheckingOptions();
@ -466,7 +466,7 @@ class Playground {
this.runOptions, this.runOptions,
this.parserOptions, this.parserOptions,
this.linterOptions, this.linterOptions,
this.formatterOptions, this.codegenOptions,
this.minifierOptions, this.minifierOptions,
this.typeCheckOptions this.typeCheckOptions
); );