mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 20:32:10 +00:00
166 lines
6 KiB
Rust
166 lines
6 KiB
Rust
use std::{
|
|
fs::{self, File},
|
|
io::Write,
|
|
path::{Path, PathBuf},
|
|
};
|
|
use walkdir::WalkDir;
|
|
|
|
use oxc_allocator::Allocator;
|
|
use oxc_formatter::{Formatter, FormatterOptions};
|
|
use oxc_parser::Parser;
|
|
use oxc_span::{SourceType, VALID_EXTENSIONS};
|
|
use oxc_tasks_common::{normalize_path, project_root};
|
|
use oxc_transformer::{TransformOptions, TransformReactOptions, TransformTarget, Transformer};
|
|
|
|
pub struct BabelOptions {
|
|
pub filter: Option<String>,
|
|
}
|
|
|
|
/// # Panics
|
|
pub fn babel(options: &BabelOptions) {
|
|
let root = project_root().join("tasks/coverage/babel/packages");
|
|
|
|
let cases = [
|
|
// ES2024
|
|
// [Regex] "babel-plugin-transform-unicode-sets-regex",
|
|
// ES2022
|
|
"babel-plugin-transform-class-properties",
|
|
"babel-plugin-transform-class-static-block",
|
|
"babel-plugin-transform-private-methods",
|
|
"babel-plugin-transform-private-property-in-object",
|
|
// [Syntax] "babel-plugin-transform-syntax-top-level-await",
|
|
// ES2021
|
|
"babel-plugin-transform-logical-assignment-operators",
|
|
"babel-plugin-transform-numeric-separator",
|
|
// ES2020
|
|
"babel-plugin-transform-export-namespace-from",
|
|
"babel-plugin-transform-dynamic-import",
|
|
"babel-plugin-transform-export-namespace-from",
|
|
"babel-plugin-transform-nullish-coalescing-operator",
|
|
"babel-plugin-transform-optional-chaining",
|
|
// [Syntax] "babel-plugin-transform-syntax-bigint",
|
|
// [Syntax] "babel-plugin-transform-syntax-dynamic-import",
|
|
// [Syntax] "babel-plugin-transform-syntax-import-meta",
|
|
// ES2019
|
|
"babel-plugin-transform-optional-catch-binding",
|
|
"babel-plugin-transform-json-strings",
|
|
// ES2018
|
|
"babel-plugin-transform-async-generator-functions",
|
|
"babel-plugin-transform-object-rest-spread",
|
|
// [Regex] "babel-plugin-transform-unicode-property-regex",
|
|
// [Regex] "babel-plugin-transform-dotall-regex",
|
|
// [Regex] "babel-plugin-transform-named-capturing-groups-regex",
|
|
// ES2017
|
|
"babel-plugin-transform-async-to-generator",
|
|
// ES2016
|
|
"babel-plugin-transform-exponentiation-operator",
|
|
// ES2015
|
|
"babel-plugin-transform-shorthand-properties",
|
|
// TypeScript
|
|
"babel-plugin-transform-typescript",
|
|
// React
|
|
"babel-plugin-transform-react-jsx",
|
|
];
|
|
|
|
let mut snapshot = String::new();
|
|
let mut total = 0;
|
|
let mut all_passed = 0;
|
|
|
|
// Get all fixtures
|
|
for case in cases {
|
|
let root = root.join(case).join("test/fixtures");
|
|
let mut paths = WalkDir::new(&root)
|
|
.into_iter()
|
|
.filter_map(Result::ok)
|
|
.filter(|e| {
|
|
e.path().file_stem().is_some_and(|name| name == "input")
|
|
&& e.path()
|
|
.extension()
|
|
.is_some_and(|ext| VALID_EXTENSIONS.contains(&ext.to_str().unwrap()))
|
|
})
|
|
.map(walkdir::DirEntry::into_path)
|
|
.collect::<Vec<_>>();
|
|
paths.sort_unstable();
|
|
total += paths.len();
|
|
|
|
// Run the test
|
|
let (passed, failed): (Vec<PathBuf>, Vec<PathBuf>) =
|
|
paths.into_iter().partition(|path| babel_test(path, options));
|
|
all_passed += passed.len();
|
|
|
|
// Snapshot
|
|
snapshot.push_str("# ");
|
|
snapshot.push_str(case);
|
|
snapshot.push('\n');
|
|
if failed.is_empty() {
|
|
snapshot.push_str("[All passed]\n");
|
|
}
|
|
for path in failed {
|
|
snapshot.push_str("* Failed: ");
|
|
snapshot.push_str(&normalize_path(path.strip_prefix(&root).unwrap()));
|
|
snapshot.push('\n');
|
|
}
|
|
for path in passed {
|
|
snapshot.push_str("* Passed: ");
|
|
snapshot.push_str(&normalize_path(path.strip_prefix(&root).unwrap()));
|
|
snapshot.push('\n');
|
|
}
|
|
snapshot.push('\n');
|
|
}
|
|
|
|
let snapshot = format!("Passed: {all_passed}/{total}\n\n{snapshot}");
|
|
let path = project_root().join("tasks/transform_conformance/babel.snap.md");
|
|
let mut file = File::create(path).unwrap();
|
|
file.write_all(snapshot.as_bytes()).unwrap();
|
|
}
|
|
|
|
fn babel_test(input_path: &Path, options: &BabelOptions) -> bool {
|
|
let output_path = input_path.parent().unwrap().read_dir().unwrap().find_map(|entry| {
|
|
let path = entry.ok()?.path();
|
|
let file_stem = path.file_stem()?;
|
|
(file_stem == "output").then_some(path)
|
|
});
|
|
let source_text = fs::read_to_string(input_path).unwrap();
|
|
let filtered =
|
|
options.filter.as_ref().is_some_and(|f| input_path.to_string_lossy().as_ref().contains(f));
|
|
|
|
if filtered {
|
|
println!("{input_path:?}");
|
|
}
|
|
|
|
let allocator = Allocator::default();
|
|
let source_type = SourceType::from_path(input_path).unwrap();
|
|
let ret = Parser::new(&allocator, &source_text, source_type).parse();
|
|
|
|
let expected = output_path.and_then(|path| fs::read_to_string(path).ok());
|
|
if let Some(expected) = &expected {
|
|
let transform_options = TransformOptions {
|
|
target: TransformTarget::ES5,
|
|
react: Some(TransformReactOptions::default()),
|
|
};
|
|
let program = allocator.alloc(ret.program);
|
|
Transformer::new(&allocator, source_type, transform_options).build(program);
|
|
|
|
let formatter_options = FormatterOptions::default();
|
|
let transformed = Formatter::new(source_text.len(), formatter_options).build(program);
|
|
let trim_transformed = remove_whitespace(&transformed);
|
|
let trim_expected = remove_whitespace(expected);
|
|
let passed = trim_transformed == trim_expected;
|
|
if filtered {
|
|
println!("Expected:\n");
|
|
println!("{expected}\n");
|
|
println!("Transformed:\n");
|
|
println!("{transformed}\n");
|
|
println!("Diff:\n");
|
|
println!("{trim_transformed:?}");
|
|
println!("{trim_expected:?}");
|
|
}
|
|
return passed;
|
|
}
|
|
|
|
ret.errors.is_empty()
|
|
}
|
|
|
|
fn remove_whitespace(s: &str) -> String {
|
|
s.replace(|c: char| c.is_ascii_whitespace(), "")
|
|
}
|