Skip to content

Commit

Permalink
feat(transform_conformance): handle deserialization errors (#2980)
Browse files Browse the repository at this point in the history
  • Loading branch information
Boshen authored Apr 14, 2024
1 parent 627dd42 commit c753c9f
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 58 deletions.
16 changes: 12 additions & 4 deletions tasks/transform_conformance/babel.snap.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Passed: 221/408
Passed: 213/408

# All Passed:
* babel-plugin-transform-react-jsx-source
Expand All @@ -25,7 +25,9 @@ Passed: 221/408
* opts/optimizeConstEnums/input.ts
* opts/rewriteImportExtensions/input.ts

# babel-plugin-transform-typescript (68/164)
# babel-plugin-transform-typescript (59/164)
* class/abstract-allowDeclareFields-false/input.ts
* class/abstract-allowDeclareFields-true/input.ts
* class/abstract-class-decorated/input.ts
* class/abstract-class-decorated-method/input.ts
* class/abstract-class-decorated-parameter/input.ts
Expand All @@ -43,6 +45,7 @@ Passed: 221/408
* class/parameter-properties-with-super/input.ts
* class/private-method-override-transform-private/input.ts
* class/transform-properties-declare-wrong-order/input.ts
* class/uninitialized-definite-babel-7/input.ts
* class/uninitialized-definite-with-declare-disabled-babel-7/input.ts
* declarations/erased/input.ts
* declarations/export-declare-enum/input.ts
Expand Down Expand Up @@ -73,16 +76,20 @@ Passed: 221/408
* imports/elision-rename/input.ts
* imports/enum-id/input.ts
* imports/enum-value/input.ts
* imports/import-named-type/input.ts
* imports/import-named-type-default-and-named/input.ts
* imports/import-removed-exceptions/input.ts
* imports/import-type/input.ts
* imports/import-type-func-with-duplicate-name/input.ts
* imports/import-type-not-removed/input.ts
* imports/import=-module/input.ts
* imports/only-remove-type-imports/input.ts
* imports/property-signature/input.ts
* imports/type-only-export-specifier-1/input.ts
* imports/type-only-export-specifier-2/input.ts
* imports/type-only-import-specifier-2/input.ts
* imports/type-only-import-specifier-3/input.ts
* imports/type-only-import-specifier-4/input.ts
* namespace/alias/input.ts
* namespace/ambient-module-nested/input.ts
* namespace/ambient-module-nested-exported/input.ts
Expand All @@ -99,6 +106,7 @@ Passed: 221/408
* namespace/export-type-only/input.ts
* namespace/module-nested/input.ts
* namespace/module-nested-export/input.ts
* namespace/multiple/input.ts
* namespace/mutable-fail/input.ts
* namespace/namespace-flag/input.ts
* namespace/namespace-nested-module/input.ts
Expand All @@ -108,6 +116,7 @@ Passed: 221/408
* namespace/nested-shorthand/input.ts
* namespace/nested-shorthand-export/input.ts
* namespace/same-name/input.ts
* namespace/undeclared/input.ts
* optimize-const-enums/custom-values/input.ts
* optimize-const-enums/custom-values-exported/input.ts
* optimize-const-enums/declare/input.ts
Expand Down Expand Up @@ -137,7 +146,7 @@ Passed: 221/408
* regression/another-preset-with-custom-jsx-keep-source-self/input.mjs
* regression/runtime-classic-allow-multiple-source-self/input.mjs

# babel-plugin-transform-react-jsx (130/161)
# babel-plugin-transform-react-jsx (131/161)
* autoImport/auto-import-react-source-type-module/input.js
* autoImport/complicated-scope-module/input.js
* autoImport/react-defined/input.js
Expand Down Expand Up @@ -168,7 +177,6 @@ Passed: 221/408
* react-automatic/should-properly-handle-keys/input.js
* react-automatic/should-throw-when-filter-is-specified/input.js
* runtime/defaults-to-classis-babel-7/input.js
* runtime/invalid-runtime/input.js

# babel-plugin-transform-react-display-name (15/16)
* display-name/nested/input.js
Expand Down
131 changes: 77 additions & 54 deletions tasks/transform_conformance/src/test_case.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use serde_json::Value;

use oxc_allocator::Allocator;
use oxc_codegen::{Codegen, CodegenOptions};
use oxc_diagnostics::Error;
use oxc_diagnostics::{miette::miette, Error};
use oxc_parser::Parser;
use oxc_semantic::SemanticBuilder;
use oxc_span::{SourceType, VALID_EXTENSIONS};
Expand Down Expand Up @@ -72,48 +72,52 @@ impl TestCaseKind {
}
}

fn transform_options(options: &BabelOptions) -> TransformOptions {
fn get_options<T: Default + DeserializeOwned>(value: Option<Value>) -> T {
value.and_then(|v| serde_json::from_value::<T>(v).ok()).unwrap_or_default()
fn transform_options(options: &BabelOptions) -> serde_json::Result<TransformOptions> {
fn get_options<T: Default + DeserializeOwned>(value: Option<Value>) -> serde_json::Result<T> {
match value {
Some(v) => serde_json::from_value::<T>(v),
None => Ok(T::default()),
}
}

let react = options.get_preset("react").map_or_else(
|| {
let jsx_plugin = options.get_plugin("transform-react-jsx");
let has_jsx_plugin = jsx_plugin.as_ref().is_some();
let mut react_options = jsx_plugin.map(get_options::<ReactOptions>).unwrap_or_default();
react_options.jsx_plugin = has_jsx_plugin;
react_options.display_name_plugin =
options.get_plugin("transform-react-display-name").is_some();
react_options.jsx_self_plugin =
options.get_plugin("transform-react-jsx-self").is_some();
react_options.jsx_source_plugin =
options.get_plugin("transform-react-jsx-source").is_some();
react_options
},
get_options::<ReactOptions>,
);

TransformOptions {
let react = if let Some(options) = options.get_preset("react") {
get_options::<ReactOptions>(options)?
} else {
let jsx_plugin = options.get_plugin("transform-react-jsx");
let has_jsx_plugin = jsx_plugin.as_ref().is_some();
let mut react_options =
jsx_plugin.map(get_options::<ReactOptions>).transpose()?.unwrap_or_default();
react_options.jsx_plugin = has_jsx_plugin;
react_options.display_name_plugin =
options.get_plugin("transform-react-display-name").is_some();
react_options.jsx_self_plugin = options.get_plugin("transform-react-jsx-self").is_some();
react_options.jsx_source_plugin =
options.get_plugin("transform-react-jsx-source").is_some();
react_options
};

Ok(TransformOptions {
assumptions: serde_json::from_value(options.assumptions.clone()).unwrap_or_default(),
decorators: options
.get_plugin("proposal-decorators")
.map(get_options::<DecoratorsOptions>)
.transpose()?
.unwrap_or_default(),
typescript: options
.get_plugin("transform-typescript")
.map(get_options::<TypeScriptOptions>)
.transpose()?
.unwrap_or_default(),
react,
}
})
}

pub trait TestCase {
fn new(path: &Path) -> Self;

fn options(&self) -> &BabelOptions;

fn transform_options(&self) -> &TransformOptions;
fn transform_options(&self) -> &serde_json::Result<TransformOptions>;

fn test(&self, filtered: bool) -> bool;

Expand All @@ -129,9 +133,10 @@ pub trait TestCase {

// Skip deprecated react options
if options.babel_8_breaking.is_some_and(|b| b) {
let react_options = &self.transform_options().react;
if react_options.use_built_ins.is_some() || react_options.use_spread.is_some() {
return true;
if let Ok(options) = self.transform_options() {
if options.react.use_built_ins.is_some() || options.react.use_spread.is_some() {
return true;
}
}
}

Expand Down Expand Up @@ -163,6 +168,13 @@ pub trait TestCase {
}

fn transform(&self, path: &Path) -> Result<String, Vec<Error>> {
let transform_options = match self.transform_options() {
Ok(transform_options) => transform_options,
Err(json_err) => {
return Err(vec![miette!(format!("{json_err:?}"))]);
}
};

let allocator = Allocator::default();
let source_text = fs::read_to_string(path).unwrap();

Expand All @@ -178,7 +190,7 @@ pub trait TestCase {

let transformed_program = allocator.alloc(ret.program);

let result = Transformer::new(&allocator, path, semantic, self.transform_options().clone())
let result = Transformer::new(&allocator, path, semantic, transform_options.clone())
.build(transformed_program);

result.map(|()| {
Expand All @@ -193,7 +205,7 @@ pub trait TestCase {
pub struct ConformanceTestCase {
path: PathBuf,
options: BabelOptions,
transform_options: TransformOptions,
transform_options: serde_json::Result<TransformOptions>,
}

impl TestCase for ConformanceTestCase {
Expand All @@ -207,7 +219,7 @@ impl TestCase for ConformanceTestCase {
&self.options
}

fn transform_options(&self) -> &TransformOptions {
fn transform_options(&self) -> &serde_json::Result<TransformOptions> {
&self.transform_options
}

Expand All @@ -230,8 +242,6 @@ impl TestCase for ConformanceTestCase {
.as_ref()
.is_some_and(|path| path.extension().and_then(std::ffi::OsStr::to_str) == Some("js"));

let transform_options = self.transform_options();

let source_type = SourceType::from_path(&self.path)
.unwrap()
.with_script(if self.options.source_type.is_some() {
Expand All @@ -246,32 +256,45 @@ impl TestCase for ConformanceTestCase {
println!("output_path: {output_path:?}");
}

// Transform input.js
let ret = Parser::new(&allocator, &input, source_type).parse();
let semantic = SemanticBuilder::new(&input, source_type)
.with_trivias(ret.trivias)
.build_module_record(PathBuf::new(), &ret.program)
.build(&ret.program)
.semantic;
let program = allocator.alloc(ret.program);
let transformer =
Transformer::new(&allocator, &self.path, semantic, transform_options.clone());

let codegen_options = CodegenOptions::default();
let mut transformed_code = String::new();
let mut actual_errors = String::new();
let result = transformer.build(program);
if result.is_ok() {
transformed_code = Codegen::<false>::new("", &input, codegen_options.clone())
.build(program)
.source_text;
} else {
actual_errors = result.err().unwrap().iter().map(ToString::to_string).collect();
}

let transform_options = match self.transform_options() {
Ok(transform_options) => {
let ret = Parser::new(&allocator, &input, source_type).parse();
let semantic = SemanticBuilder::new(&input, source_type)
.with_trivias(ret.trivias)
.build_module_record(PathBuf::new(), &ret.program)
.build(&ret.program)
.semantic;
let program = allocator.alloc(ret.program);
let transformer =
Transformer::new(&allocator, &self.path, semantic, transform_options.clone());
let result = transformer.build(program);
if result.is_ok() {
transformed_code = Codegen::<false>::new("", &input, codegen_options.clone())
.build(program)
.source_text;
} else {
actual_errors = result.err().unwrap().iter().map(ToString::to_string).collect();
}
Some(transform_options.clone())
}
Err(json_err) => {
let error = format!("{json_err:?}");
if error.contains("expected `classic` or `automatic`") {
actual_errors.push_str(r#"Runtime must be either "classic" or "automatic"."#);
} else {
actual_errors.push_str(&error);
}
None
}
};

let babel_options = self.options();

// Get output.js by using our codeg so code comparison can match.
// Get output.js by using our code gen so code comparison can match.
let output = output_path.and_then(|path| fs::read_to_string(path).ok()).map_or_else(
|| {
if let Some(throws) = &babel_options.throws {
Expand Down Expand Up @@ -324,7 +347,7 @@ impl TestCase for ConformanceTestCase {
pub struct ExecTestCase {
path: PathBuf,
options: BabelOptions,
transform_options: TransformOptions,
transform_options: serde_json::Result<TransformOptions>,
}

impl ExecTestCase {
Expand Down Expand Up @@ -368,7 +391,7 @@ impl TestCase for ExecTestCase {
&self.options
}

fn transform_options(&self) -> &TransformOptions {
fn transform_options(&self) -> &serde_json::Result<TransformOptions> {
&self.transform_options
}

Expand Down

0 comments on commit c753c9f

Please sign in to comment.