mirror of
https://github.com/danbulant/oxc
synced 2026-05-25 12:51:57 +00:00
feat(transformer): add babel conformance test suite (#920)
This commit is contained in:
parent
4bf329e1cf
commit
f4cea34534
10 changed files with 107 additions and 6 deletions
1
.github/workflows/conformance.yml
vendored
1
.github/workflows/conformance.yml
vendored
|
|
@ -49,6 +49,7 @@ jobs:
|
||||||
uses: ./.github/actions/rustup
|
uses: ./.github/actions/rustup
|
||||||
|
|
||||||
- run: cargo coverage
|
- run: cargo coverage
|
||||||
|
- run: cargo run --release -p oxc_transform_conformance
|
||||||
|
|
||||||
# - run: cargo minsize
|
# - run: cargo minsize
|
||||||
|
|
||||||
|
|
|
||||||
13
Cargo.lock
generated
13
Cargo.lock
generated
|
|
@ -1719,6 +1719,19 @@ dependencies = [
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "oxc_transform_conformance"
|
||||||
|
version = "0.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"oxc_allocator",
|
||||||
|
"oxc_formatter",
|
||||||
|
"oxc_parser",
|
||||||
|
"oxc_span",
|
||||||
|
"oxc_tasks_common",
|
||||||
|
"oxc_transformer",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxc_transformer"
|
name = "oxc_transformer"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
|
|
||||||
|
|
@ -848,11 +848,11 @@ impl Gen for RegExpLiteral {
|
||||||
|
|
||||||
impl Gen for StringLiteral {
|
impl Gen for StringLiteral {
|
||||||
fn gen(&self, p: &mut Formatter) {
|
fn gen(&self, p: &mut Formatter) {
|
||||||
p.print(b'\'');
|
p.print_quote();
|
||||||
for c in self.value.chars() {
|
for c in self.value.chars() {
|
||||||
p.print_str(c.escape_default().to_string().as_bytes());
|
p.print_str(c.escape_default().to_string().as_bytes());
|
||||||
}
|
}
|
||||||
p.print(b'\'');
|
p.print_quote();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,12 +54,14 @@ pub enum FinalEndOfLine {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct FormatterOptions {
|
pub struct FormatterOptions {
|
||||||
pub indentation: u8,
|
pub indentation: u8,
|
||||||
|
// <https://prettier.io/docs/en/options#quotes>
|
||||||
|
pub single_quote: bool,
|
||||||
pub end_of_line: EndOfLine,
|
pub end_of_line: EndOfLine,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FormatterOptions {
|
impl Default for FormatterOptions {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self { indentation: 4, end_of_line: EndOfLine::LF }
|
Self { indentation: 4, single_quote: false, end_of_line: EndOfLine::LF }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,12 +70,14 @@ impl Default for FormatterOptions {
|
||||||
pub struct InnerOptions {
|
pub struct InnerOptions {
|
||||||
pub indentation: u8,
|
pub indentation: u8,
|
||||||
pub end_of_line: FinalEndOfLine,
|
pub end_of_line: FinalEndOfLine,
|
||||||
|
pub single_quote: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<FormatterOptions> for InnerOptions {
|
impl From<FormatterOptions> for InnerOptions {
|
||||||
fn from(options: FormatterOptions) -> Self {
|
fn from(options: FormatterOptions) -> Self {
|
||||||
Self {
|
Self {
|
||||||
indentation: options.indentation,
|
indentation: options.indentation,
|
||||||
|
single_quote: options.single_quote,
|
||||||
end_of_line: options.end_of_line.get_final_end_of_line(),
|
end_of_line: options.end_of_line.get_final_end_of_line(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -208,6 +212,11 @@ impl Formatter {
|
||||||
self.print(b'=');
|
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) {
|
pub fn print_indent(&mut self) {
|
||||||
for _ in 0..self.indentation {
|
for _ in 0..self.indentation {
|
||||||
self.print(b' ');
|
self.print(b' ');
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ impl<'a> OptionalCatchBinding<'a> {
|
||||||
if clause.param.is_some() {
|
if clause.param.is_some() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let binding_identifier = BindingIdentifier::new(Span::default(), "unused".into());
|
let binding_identifier = BindingIdentifier::new(Span::default(), "_unused".into());
|
||||||
let binding_pattern_kind = self.ast.binding_identifier(binding_identifier);
|
let binding_pattern_kind = self.ast.binding_identifier(binding_identifier);
|
||||||
let binding_pattern = self.ast.binding_pattern(binding_pattern_kind, None, false);
|
let binding_pattern = self.ast.binding_pattern(binding_pattern_kind, None, false);
|
||||||
clause.param = Some(binding_pattern);
|
clause.param = Some(binding_pattern);
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,13 @@ use es2019::OptionalCatchBinding;
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct TransformOptions {
|
pub struct TransformOptions {
|
||||||
target: TransformTarget,
|
pub target: TransformTarget,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See <https://www.typescriptlang.org/tsconfig#target>
|
/// See <https://www.typescriptlang.org/tsconfig#target>
|
||||||
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
|
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
pub enum TransformTarget {
|
pub enum TransformTarget {
|
||||||
|
ES2015,
|
||||||
ES2016,
|
ES2016,
|
||||||
ES2019,
|
ES2019,
|
||||||
#[default]
|
#[default]
|
||||||
|
|
|
||||||
3
justfile
3
justfile
|
|
@ -56,9 +56,10 @@ test:
|
||||||
lint:
|
lint:
|
||||||
cargo lint -- --deny warnings
|
cargo lint -- --deny warnings
|
||||||
|
|
||||||
# Run all the conformance tests. See `tasks/coverage`, `tasks/minsize`
|
# Run all the conformance tests. See `tasks/coverage`, `tasks/transform_conformance`, `tasks/minsize`
|
||||||
coverage:
|
coverage:
|
||||||
cargo coverage
|
cargo coverage
|
||||||
|
cargo run --release -p oxc_transform_conformance
|
||||||
# cargo minsize
|
# cargo minsize
|
||||||
|
|
||||||
# Get code coverage
|
# Get code coverage
|
||||||
|
|
|
||||||
21
tasks/transform_conformance/Cargo.toml
Normal file
21
tasks/transform_conformance/Cargo.toml
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
[package]
|
||||||
|
name = "oxc_transform_conformance"
|
||||||
|
version = "0.0.0"
|
||||||
|
publish = false
|
||||||
|
authors.workspace = true
|
||||||
|
description.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
keywords.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
oxc_span = { workspace = true }
|
||||||
|
oxc_allocator = { workspace = true }
|
||||||
|
oxc_parser = { workspace = true }
|
||||||
|
oxc_formatter = { workspace = true }
|
||||||
|
oxc_transformer = { workspace = true }
|
||||||
|
oxc_tasks_common = { workspace = true }
|
||||||
|
|
||||||
|
walkdir = { workspace = true }
|
||||||
46
tasks/transform_conformance/src/lib.rs
Normal file
46
tasks/transform_conformance/src/lib.rs
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
use std::{fs, path::Path};
|
||||||
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
|
use oxc_allocator::Allocator;
|
||||||
|
use oxc_formatter::{Formatter, FormatterOptions};
|
||||||
|
use oxc_parser::Parser;
|
||||||
|
use oxc_span::SourceType;
|
||||||
|
use oxc_tasks_common::project_root;
|
||||||
|
use oxc_transformer::{TransformOptions, TransformTarget, Transformer};
|
||||||
|
|
||||||
|
/// # Panics
|
||||||
|
pub fn babel(name: &str) {
|
||||||
|
let root = project_root().join("tasks/coverage/babel/packages");
|
||||||
|
let root = root.join(name).join("test/fixtures");
|
||||||
|
let paths = WalkDir::new(root)
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(Result::ok)
|
||||||
|
.filter(|e| e.file_name() == "input.js")
|
||||||
|
.map(|e| e.path().parent().unwrap().to_path_buf())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
for path in paths {
|
||||||
|
babel_test(&path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn babel_test(path: &Path) {
|
||||||
|
let input_path = path.join("input.js");
|
||||||
|
let output_path = path.join("output.js");
|
||||||
|
let source_text = fs::read_to_string(&input_path).unwrap();
|
||||||
|
let expected = fs::read_to_string(output_path).unwrap();
|
||||||
|
|
||||||
|
let allocator = Allocator::default();
|
||||||
|
let source_type = SourceType::from_path(&input_path).unwrap();
|
||||||
|
let ret = Parser::new(&allocator, &source_text, source_type).parse();
|
||||||
|
|
||||||
|
let transform_options = TransformOptions { target: TransformTarget::ES2015 };
|
||||||
|
let program = allocator.alloc(ret.program);
|
||||||
|
Transformer::new(&allocator, &transform_options).build(program);
|
||||||
|
|
||||||
|
let formatter_options = FormatterOptions::default();
|
||||||
|
let printed = Formatter::new(source_text.len(), formatter_options).build(program);
|
||||||
|
|
||||||
|
let printed = printed.replace(|c: char| c.is_ascii_whitespace(), "");
|
||||||
|
let expected = expected.replace(|c: char| c.is_ascii_whitespace(), "");
|
||||||
|
assert_eq!(printed, expected, "{path:?}");
|
||||||
|
}
|
||||||
9
tasks/transform_conformance/src/main.rs
Normal file
9
tasks/transform_conformance/src/main.rs
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
use oxc_transform_conformance::babel;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let names = ["babel-plugin-transform-optional-catch-binding"];
|
||||||
|
for name in names {
|
||||||
|
babel(name);
|
||||||
|
println!("Passed: {name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue