refactor(transformer): deserialize BabelOptions::plugins (#7045)

This commit is contained in:
Boshen 2024-11-01 05:44:57 +00:00
parent 4bef99c80b
commit 52c20d633c
9 changed files with 287 additions and 219 deletions

View file

@ -136,7 +136,7 @@ use oxc_syntax::{
}; };
use oxc_traverse::{Ancestor, BoundIdentifier, Traverse, TraverseCtx}; use oxc_traverse::{Ancestor, BoundIdentifier, Traverse, TraverseCtx};
#[derive(Debug, Default, Clone, Deserialize)] #[derive(Debug, Default, Clone, Copy, Deserialize)]
pub struct ArrowFunctionsOptions { pub struct ArrowFunctionsOptions {
/// This option enables the following: /// This option enables the following:
/// * Wrap the generated function in .bind(this) and keeps uses of this inside the function as-is, instead of using a renamed this. /// * Wrap the generated function in .bind(this) and keeps uses of this inside the function as-is, instead of using a renamed this.

View file

@ -17,9 +17,7 @@ pub struct ES2015<'a> {
impl<'a> ES2015<'a> { impl<'a> ES2015<'a> {
pub fn new(options: ES2015Options) -> Self { pub fn new(options: ES2015Options) -> Self {
Self { Self {
arrow_functions: ArrowFunctions::new( arrow_functions: ArrowFunctions::new(options.arrow_function.unwrap_or_default()),
options.arrow_function.clone().unwrap_or_default(),
),
options, options,
} }
} }

View file

@ -2,9 +2,14 @@ mod env;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use serde::Deserialize; use serde::{de::DeserializeOwned, Deserialize};
use serde_json::Value; use serde_json::Value;
use crate::{
es2015::ArrowFunctionsOptions, es2018::ObjectRestSpreadOptions, es2022::ClassPropertiesOptions,
jsx::JsxOptions, TypeScriptOptions,
};
pub use env::{BabelEnvOptions, Targets}; pub use env::{BabelEnvOptions, Targets};
/// Babel options /// Babel options
@ -20,7 +25,7 @@ pub struct BabelOptions {
// Plugin and Preset options // Plugin and Preset options
#[serde(default)] #[serde(default)]
pub plugins: Vec<Value>, // Can be a string or an array pub plugins: BabelPlugins,
#[serde(default)] #[serde(default)]
pub presets: Vec<Value>, // Can be a string or an array pub presets: Vec<Value>, // Can be a string or an array
@ -77,61 +82,62 @@ impl BabelOptions {
/// Read options.json and merge them with options.json from ancestors directories. /// Read options.json and merge them with options.json from ancestors directories.
/// # Panics /// # Panics
pub fn from_test_path(path: &Path) -> Self { pub fn from_test_path(path: &Path) -> Self {
let mut options_json: Option<Self> = None; let mut babel_options: Option<Self> = None;
let mut plugins_json = None;
for path in path.ancestors().take(3) { for path in path.ancestors().take(3) {
let file = path.join("options.json"); let file = path.join("options.json");
if !file.exists() { if !file.exists() {
continue; continue;
} }
let file = std::fs::read_to_string(&file).unwrap();
let new_json: Self = serde_json::from_str(&file).unwrap(); let content = std::fs::read_to_string(&file).unwrap();
if let Some(existing_json) = options_json.as_mut() { let mut new_value = serde_json::from_str::<serde_json::Value>(&content).unwrap();
if existing_json.source_type.is_none() {
if let Some(source_type) = new_json.source_type { let new_plugins = new_value.as_object_mut().unwrap().remove("plugins");
existing_json.source_type = Some(source_type); if plugins_json.is_none() {
plugins_json = new_plugins;
}
let new_options: Self = serde_json::from_value::<BabelOptions>(new_value)
.unwrap_or_else(|err| panic!("{err:?}\n{file:?}\n{content}"));
if let Some(existing_options) = babel_options.as_mut() {
if existing_options.source_type.is_none() {
if let Some(source_type) = new_options.source_type {
existing_options.source_type = Some(source_type);
} }
} }
if existing_json.throws.is_none() { if existing_options.throws.is_none() {
if let Some(throws) = new_json.throws { if let Some(throws) = new_options.throws {
existing_json.throws = Some(throws); existing_options.throws = Some(throws);
} }
} }
if existing_json.plugins.is_empty() { if existing_options.presets.is_empty() {
existing_json.plugins = new_json.plugins; existing_options.presets = new_options.presets;
}
if existing_json.presets.is_empty() {
existing_json.presets = new_json.presets;
} }
} else { } else {
options_json = Some(new_json); babel_options = Some(new_options);
} }
} }
options_json.unwrap_or_default()
let mut options = babel_options.unwrap_or_default();
if let Some(plugins_json) = plugins_json {
options.plugins = serde_json::from_value::<BabelPlugins>(plugins_json).unwrap();
}
options
} }
pub fn is_jsx(&self) -> bool { pub fn is_jsx(&self) -> bool {
self.plugins.iter().any(|v| v.as_str().is_some_and(|v| v == "jsx")) self.plugins.syntax_jsx
} }
pub fn is_typescript(&self) -> bool { pub fn is_typescript(&self) -> bool {
self.plugins.iter().any(|v| { self.plugins.syntax_typescript.is_some()
let string_value = v.as_str().is_some_and(|v| v == "typescript");
let array_value = v.get(0).and_then(Value::as_str).is_some_and(|s| s == "typescript");
string_value || array_value
})
} }
pub fn is_typescript_definition(&self) -> bool { pub fn is_typescript_definition(&self) -> bool {
self.plugins.iter().filter_map(Value::as_array).any(|p| { self.plugins.syntax_typescript.is_some_and(|o| o.dts)
let typescript = p.first().and_then(Value::as_str).is_some_and(|s| s == "typescript");
let dts = p
.get(1)
.and_then(Value::as_object)
.and_then(|v| v.get("dts"))
.and_then(Value::as_bool)
.is_some_and(|v| v);
typescript && dts
})
} }
pub fn is_module(&self) -> bool { pub fn is_module(&self) -> bool {
@ -142,22 +148,10 @@ impl BabelOptions {
self.source_type.as_ref().map_or(false, |s| s.as_str() == "unambiguous") self.source_type.as_ref().map_or(false, |s| s.as_str() == "unambiguous")
} }
/// Returns
/// * `Some<None>` if the plugin exists without a config
/// * `Some<Some<Value>>` if the plugin exists with a config
/// * `None` if the plugin does not exist
pub fn get_plugin(&self, name: &str) -> Option<Option<Value>> {
self.plugins.iter().find_map(|v| Self::get_value(v, name))
}
pub fn get_preset(&self, name: &str) -> Option<Option<Value>> { pub fn get_preset(&self, name: &str) -> Option<Option<Value>> {
self.presets.iter().find_map(|v| Self::get_value(v, name)) self.presets.iter().find_map(|v| Self::get_value(v, name))
} }
pub fn has_plugin(&self, name: &str) -> bool {
self.get_plugin(name).is_some()
}
pub fn has_preset(&self, name: &str) -> bool { pub fn has_preset(&self, name: &str) -> bool {
self.get_preset(name).is_some() self.get_preset(name).is_some()
} }
@ -173,3 +167,167 @@ impl BabelOptions {
} }
} }
} }
#[derive(Debug, Default, Clone, Copy, Deserialize)]
pub struct SyntaxTypeScriptOptions {
#[serde(default)]
pub dts: bool,
}
#[derive(Debug, Default, Clone, Deserialize)]
pub struct SyntaxDecoratorOptions {
#[serde(default)]
pub version: String,
}
#[derive(Debug, Default, Clone, Deserialize)]
#[serde(try_from = "PluginPresetEntries")]
pub struct BabelPlugins {
pub errors: Vec<String>,
pub unsupported: Vec<String>,
// syntax
pub syntax_typescript: Option<SyntaxTypeScriptOptions>,
pub syntax_jsx: bool,
// decorators
pub syntax_decorators: Option<SyntaxDecoratorOptions>,
pub proposal_decorators: Option<SyntaxDecoratorOptions>,
// ts
pub typescript: Option<TypeScriptOptions>,
// jsx
pub react_jsx: Option<JsxOptions>,
pub react_jsx_dev: Option<JsxOptions>,
pub react_jsx_self: bool,
pub react_jsx_source: bool,
pub react_display_name: bool,
// regexp
pub sticky_flag: bool,
pub unicode_flag: bool,
pub dot_all_flag: bool,
pub look_behind_assertions: bool,
pub named_capture_groups: bool,
pub unicode_property_escapes: bool,
pub match_indices: bool,
/// Enables plugin to transform the RegExp literal has `v` flag
pub set_notation: bool,
// ES2015
pub arrow_function: Option<ArrowFunctionsOptions>,
// ES2016
pub exponentiation_operator: bool,
// ES2017
pub async_to_generator: bool,
// ES2018
pub object_rest_spread: Option<ObjectRestSpreadOptions>,
pub async_generator_functions: bool,
// ES2019
pub optional_catch_binding: bool,
// ES2020
pub nullish_coalescing_operator: bool,
// ES2021
pub logical_assignment_operators: bool,
// ES2022
pub class_static_block: bool,
pub class_properties: Option<ClassPropertiesOptions>,
}
/// <https://babeljs.io/docs/options#pluginpreset-entries>
#[derive(Debug, Deserialize)]
struct PluginPresetEntries(Vec<PluginPresetEntry>);
impl TryFrom<PluginPresetEntries> for BabelPlugins {
type Error = String;
fn try_from(entries: PluginPresetEntries) -> Result<Self, Self::Error> {
let mut p = BabelPlugins::default();
for entry in entries.0 {
match entry.name() {
"typescript" | "syntax-typescript" => {
p.syntax_typescript = Some(entry.value::<SyntaxTypeScriptOptions>()?);
}
"jsx" | "syntax-jsx" => p.syntax_jsx = true,
"syntax-decorators" => {
p.syntax_decorators = Some(entry.value::<SyntaxDecoratorOptions>()?);
}
"proposal-decorators" => {
p.proposal_decorators = Some(entry.value::<SyntaxDecoratorOptions>()?);
}
"transform-typescript" => {
p.typescript =
entry.value::<TypeScriptOptions>().map_err(|err| p.errors.push(err)).ok();
}
"transform-react-jsx" => {
p.react_jsx =
entry.value::<JsxOptions>().map_err(|err| p.errors.push(err)).ok();
}
"transform-react-jsx-development" => {
p.react_jsx_dev =
entry.value::<JsxOptions>().map_err(|err| p.errors.push(err)).ok();
}
"transform-react-display-name" => p.react_display_name = true,
"transform-react-jsx-self" => p.react_jsx_self = true,
"transform-react-jsx-source" => p.react_jsx_source = true,
"transform-sticky-regex" => p.sticky_flag = true,
"transform-unicode-regex" => p.unicode_flag = true,
"transform-dotall-regex" => p.dot_all_flag = true,
"esbuild-regexp-lookbehind-assertions" => p.look_behind_assertions = true,
"transform-named-capturing-groups-regex" => p.named_capture_groups = true,
"transform-unicode-property-regex" => p.unicode_property_escapes = true,
"esbuild-regexp-match-indices" => p.match_indices = true,
"transform-unicode-sets-regex" => p.set_notation = true,
"transform-arrow-functions" => {
p.arrow_function = entry
.value::<ArrowFunctionsOptions>()
.map_err(|err| p.errors.push(err))
.ok();
}
"transform-exponentiation-operator" => p.exponentiation_operator = true,
"transform-async-to-generator" => p.async_to_generator = true,
"transform-object-rest-spread" => {
p.object_rest_spread = entry
.value::<ObjectRestSpreadOptions>()
.inspect_err(|err| p.errors.push(err.to_string()))
.ok();
}
"transform-async-generator-functions" => p.async_generator_functions = true,
"transform-optional-catch-binding" => p.optional_catch_binding = true,
"transform-nullish-coalescing-operator" => p.nullish_coalescing_operator = true,
"transform-logical-assignment-operators" => p.logical_assignment_operators = true,
"transform-class-static-block" => p.class_static_block = true,
"transform-class-properties" => {
p.class_properties = entry
.value::<ClassPropertiesOptions>()
.inspect_err(|err| p.errors.push(err.to_string()))
.ok();
}
s => p.unsupported.push(s.to_string()),
}
}
Ok(p)
}
}
/// <https://babeljs.io/docs/options#pluginpreset-entries>
#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum PluginPresetEntry {
String(String),
Vec1([String; 1]),
Tuple(String, serde_json::Value),
}
impl PluginPresetEntry {
fn name(&self) -> &str {
match self {
Self::String(s) | Self::Tuple(s, _) => s,
Self::Vec1(s) => &s[0],
}
}
fn value<T: DeserializeOwned + Default>(self) -> Result<T, String> {
match self {
Self::String(_) | Self::Vec1(_) => Ok(T::default()),
Self::Tuple(name, v) => {
serde_json::from_value::<T>(v).map_err(|err| format!("{name}: {err}"))
}
}
}
}

View file

@ -1,14 +1,8 @@
use oxc_diagnostics::{Error, OxcDiagnostic}; use oxc_diagnostics::{Error, OxcDiagnostic};
use crate::{ use crate::{
es2015::{ArrowFunctionsOptions, ES2015Options}, es2015::ES2015Options, es2016::ES2016Options, es2017::ES2017Options, es2018::ES2018Options,
es2016::ES2016Options, es2019::ES2019Options, es2020::ES2020Options, es2021::ES2021Options, es2022::ES2022Options,
es2017::ES2017Options,
es2018::{ES2018Options, ObjectRestSpreadOptions},
es2019::ES2019Options,
es2020::ES2020Options,
es2021::ES2021Options,
es2022::{ClassPropertiesOptions, ES2022Options},
regexp::RegExpOptions, regexp::RegExpOptions,
}; };
@ -146,117 +140,59 @@ impl TryFrom<&BabelOptions> for EnvOptions {
.unwrap_or_default(); .unwrap_or_default();
let regexp = RegExpOptions { let regexp = RegExpOptions {
sticky_flag: env.regexp.sticky_flag || options.has_plugin("transform-sticky-regex"), sticky_flag: env.regexp.sticky_flag || options.plugins.sticky_flag,
unicode_flag: env.regexp.unicode_flag || options.has_plugin("transform-unicode-regex"), unicode_flag: env.regexp.unicode_flag || options.plugins.unicode_flag,
dot_all_flag: env.regexp.dot_all_flag || options.has_plugin("transform-dotall-regex"), dot_all_flag: env.regexp.dot_all_flag || options.plugins.dot_all_flag,
look_behind_assertions: env.regexp.look_behind_assertions, look_behind_assertions: env.regexp.look_behind_assertions
|| options.plugins.look_behind_assertions,
named_capture_groups: env.regexp.named_capture_groups named_capture_groups: env.regexp.named_capture_groups
|| options.has_plugin("transform-named-capturing-groups-regex"), || options.plugins.named_capture_groups,
unicode_property_escapes: env.regexp.unicode_property_escapes unicode_property_escapes: env.regexp.unicode_property_escapes
|| options.has_plugin("transform-unicode-property-regex"), || options.plugins.unicode_property_escapes,
match_indices: env.regexp.match_indices, match_indices: env.regexp.match_indices,
set_notation: env.regexp.set_notation set_notation: env.regexp.set_notation || options.plugins.set_notation,
|| options.has_plugin("transform-unicode-sets-regex"),
}; };
let es2015 = ES2015Options { let es2015 = ES2015Options {
arrow_function: { arrow_function: options.plugins.arrow_function.or(env.es2015.arrow_function),
let plugin_name = "transform-arrow-functions";
options
.get_plugin(plugin_name)
.map(|o| {
o.and_then(|options| {
serde_json::from_value::<ArrowFunctionsOptions>(options)
.inspect_err(|err| {
report_error(plugin_name, err, false, &mut errors);
})
.ok()
})
.unwrap_or_default()
})
.or(env.es2015.arrow_function)
},
}; };
let es2016 = ES2016Options { let es2016 = ES2016Options {
exponentiation_operator: { exponentiation_operator: options.plugins.exponentiation_operator
let plugin_name = "transform-exponentiation-operator"; || env.es2016.exponentiation_operator,
options.get_plugin(plugin_name).is_some() || env.es2016.exponentiation_operator
},
}; };
let es2017 = ES2017Options { let es2017 = ES2017Options {
async_to_generator: { async_to_generator: options.plugins.async_to_generator || env.es2017.async_to_generator,
let plugin_name = "transform-async-to-generator";
options.get_plugin(plugin_name).is_some() || env.es2017.async_to_generator
},
}; };
let es2018 = ES2018Options { let es2018 = ES2018Options {
object_rest_spread: { object_rest_spread: options
let plugin_name = "transform-object-rest-spread"; .plugins
options .object_rest_spread
.get_plugin(plugin_name) .or(env.es2018.object_rest_spread),
.map(|o| { async_generator_functions: options.plugins.async_generator_functions
o.and_then(|options| { || env.es2018.async_generator_functions,
serde_json::from_value::<ObjectRestSpreadOptions>(options)
.inspect_err(|err| {
report_error(plugin_name, err, false, &mut errors);
})
.ok()
})
.unwrap_or_default()
})
.or(env.es2018.object_rest_spread)
},
async_generator_functions: {
let plugin_name = "transform-async-generator-functions";
options.get_plugin(plugin_name).is_some() || env.es2018.async_generator_functions
},
}; };
let es2019 = ES2019Options { let es2019 = ES2019Options {
optional_catch_binding: { optional_catch_binding: options.plugins.optional_catch_binding
let plugin_name = "transform-optional-catch-binding"; || env.es2019.optional_catch_binding,
options.get_plugin(plugin_name).is_some() || env.es2019.optional_catch_binding
},
}; };
let es2020 = ES2020Options { let es2020 = ES2020Options {
nullish_coalescing_operator: { nullish_coalescing_operator: options.plugins.nullish_coalescing_operator
let plugin_name = "transform-nullish-coalescing-operator"; || env.es2020.nullish_coalescing_operator,
options.get_plugin(plugin_name).is_some() || env.es2020.nullish_coalescing_operator
},
}; };
let es2021 = ES2021Options { let es2021 = ES2021Options {
logical_assignment_operators: { logical_assignment_operators: options.plugins.logical_assignment_operators
let plugin_name = "transform-logical-assignment-operators"; || env.es2021.logical_assignment_operators,
options.get_plugin(plugin_name).is_some() || env.es2021.logical_assignment_operators
},
}; };
let es2022 = ES2022Options { let es2022 = ES2022Options {
class_static_block: { class_static_block: options.plugins.class_static_block || env.es2022.class_static_block,
let plugin_name = "transform-class-static-block"; class_properties: options.plugins.class_properties.or(env.es2022.class_properties),
options.get_plugin(plugin_name).is_some() || env.es2022.class_static_block
},
class_properties: {
let plugin_name = "transform-class-properties";
options
.get_plugin(plugin_name)
.map(|o| {
o.and_then(|options| {
serde_json::from_value::<ClassPropertiesOptions>(options)
.inspect_err(|err| {
report_error(plugin_name, err, false, &mut errors);
})
.ok()
})
.unwrap_or_default()
})
.or(env.es2022.class_properties)
},
}; };
if !errors.is_empty() { if !errors.is_empty() {

View file

@ -72,6 +72,7 @@ impl TryFrom<&BabelOptions> for TransformOptions {
/// If the `options` contains any unknown fields, they will be returned as a list of errors. /// If the `options` contains any unknown fields, they will be returned as a list of errors.
fn try_from(options: &BabelOptions) -> Result<Self, Self::Error> { fn try_from(options: &BabelOptions) -> Result<Self, Self::Error> {
let mut errors = Vec::<Error>::new(); let mut errors = Vec::<Error>::new();
errors.extend(options.plugins.errors.iter().map(|err| Error::msg(err.clone())));
let assumptions = if options.assumptions.is_null() { let assumptions = if options.assumptions.is_null() {
CompilerAssumptions::default() CompilerAssumptions::default()
@ -92,15 +93,7 @@ impl TryFrom<&BabelOptions> for TransformOptions {
.unwrap_or_default() .unwrap_or_default()
}) })
} else { } else {
options.get_plugin("transform-typescript").and_then(|options| { options.plugins.typescript.clone()
options
.map(|options| {
serde_json::from_value::<TypeScriptOptions>(options)
.inspect_err(|err| report_error("typescript", err, false, &mut errors))
.ok()
})
.unwrap_or_default()
})
} }
.unwrap_or_default(); .unwrap_or_default();
@ -109,35 +102,19 @@ impl TryFrom<&BabelOptions> for TransformOptions {
.inspect_err(|err| report_error("react", err, true, &mut errors)) .inspect_err(|err| report_error("react", err, true, &mut errors))
.unwrap_or_default() .unwrap_or_default()
} else { } else {
let jsx_plugin_name = "transform-react-jsx"; let mut jsx_options = if let Some(options) = &options.plugins.react_jsx_dev {
let jsx_dev_name = "transform-react-jsx-development"; options.clone()
let has_jsx_plugin = options.has_plugin(jsx_plugin_name); } else if let Some(options) = &options.plugins.react_jsx {
let mut react_options = if has_jsx_plugin { options.clone()
options.get_plugin(jsx_plugin_name).and_then(|options| {
options.and_then(|options| {
serde_json::from_value::<JsxOptions>(options)
.inspect_err(|err| {
report_error(jsx_plugin_name, err, false, &mut errors);
})
.ok()
})
})
} else { } else {
options.get_plugin(jsx_dev_name).and_then(|options| { JsxOptions::default()
options.and_then(|options| { };
serde_json::from_value::<JsxOptions>(options) jsx_options.development = options.plugins.react_jsx_dev.is_some();
.inspect_err(|err| report_error(jsx_dev_name, err, false, &mut errors)) jsx_options.jsx_plugin = options.plugins.react_jsx.is_some();
.ok() jsx_options.display_name_plugin = options.plugins.react_display_name;
}) jsx_options.jsx_self_plugin = options.plugins.react_jsx_self;
}) jsx_options.jsx_source_plugin = options.plugins.react_jsx_source;
} jsx_options
.unwrap_or_default();
react_options.development = options.has_plugin(jsx_dev_name);
react_options.jsx_plugin = has_jsx_plugin;
react_options.display_name_plugin = options.has_plugin("transform-react-display-name");
react_options.jsx_self_plugin = options.has_plugin("transform-react-jsx-self");
react_options.jsx_source_plugin = options.has_plugin("transform-react-jsx-source");
react_options
}; };
let env = match EnvOptions::try_from(options) { let env = match EnvOptions::try_from(options) {
@ -177,17 +154,3 @@ fn report_error(name: &str, err: &serde_json::Error, is_preset: bool, errors: &m
if is_preset { format!("preset-{name}: {err}",) } else { format!("{name}: {err}",) }; if is_preset { format!("preset-{name}: {err}",) } else { format!("{name}: {err}",) };
errors.push(OxcDiagnostic::error(message).into()); errors.push(OxcDiagnostic::error(message).into());
} }
#[test]
fn test_deny_unknown_fields() {
let options = serde_json::json!({
"plugins": [["transform-react-jsx", { "runtime": "automatic", "filter": 1 }]],
"sourceType": "module"
});
let babel_options = serde_json::from_value::<BabelOptions>(options).unwrap();
let result = TransformOptions::try_from(&babel_options);
assert!(result.is_err());
let err_message =
result.err().unwrap().iter().map(ToString::to_string).collect::<Vec<_>>().join("\n");
assert!(err_message.contains("transform-react-jsx: unknown field `filter`"));
}

View file

@ -3,7 +3,7 @@ commit: d20b314c
parser_babel Summary: parser_babel Summary:
AST Parsed : 2126/2136 (99.53%) AST Parsed : 2126/2136 (99.53%)
Positive Passed: 2116/2136 (99.06%) Positive Passed: 2116/2136 (99.06%)
Negative Passed: 1387/1500 (92.47%) Negative Passed: 1389/1506 (92.23%)
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/annex-b/enabled/3.1-sloppy-labeled-functions-if-body/input.js Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/annex-b/enabled/3.1-sloppy-labeled-functions-if-body/input.js
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/core/categorized/invalid-fn-decl-labeled-inside-if/input.js Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/core/categorized/invalid-fn-decl-labeled-inside-if/input.js
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/core/categorized/invalid-fn-decl-labeled-inside-loop/input.js Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/core/categorized/invalid-fn-decl-labeled-inside-loop/input.js
@ -33,6 +33,10 @@ Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/es
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0276/input.js Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0276/input.js
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/flow/expect-plugin/export-interface/input.js Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/flow/expect-plugin/export-interface/input.js
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/flow/expect-plugin/export-type/input.js Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/flow/expect-plugin/export-type/input.js
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/jsx/errors/_no-plugin-fragment/input.js
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/jsx/errors/_no-plugin-jsx-expression/input.js
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/jsx/errors/_no_plugin/input.js
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/jsx/errors/_no_plugin-non-BMP-identifier/input.js
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/cast/satisfies-const-error/input.ts Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/cast/satisfies-const-error/input.ts
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/cast/unparenthesized-assert-and-assign/input.ts Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/cast/unparenthesized-assert-and-assign/input.ts
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/cast/unparenthesized-type-assertion-and-assign/input.ts Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/cast/unparenthesized-type-assertion-and-assign/input.ts
@ -9732,11 +9736,22 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
· ─ · ─
╰──── ╰────
× Expected `<` but found `EOF`
╭─[babel/packages/babel-parser/test/fixtures/jsx/errors/_no-plugin-type-param/input.js:2:1]
1 │ <div>() => {}
╰────
× Expected `<` but found `EOF` × Expected `<` but found `EOF`
╭─[babel/packages/babel-parser/test/fixtures/jsx/errors/unclosed-jsx-element/input.js:2:1] ╭─[babel/packages/babel-parser/test/fixtures/jsx/errors/unclosed-jsx-element/input.js:2:1]
1 │ <div>() => {} 1 │ <div>() => {}
╰──── ╰────
× Unexpected token
╭─[babel/packages/babel-parser/test/fixtures/placeholders/_errors/no-plugin/input.js:1:1]
1 │ %%FOO%%
· ─
╰────
× Bad escape sequence in untagged template literal × Bad escape sequence in untagged template literal
╭─[babel/packages/babel-parser/test/fixtures/tokens/template-string/invalid-octal/input.js:1:2] ╭─[babel/packages/babel-parser/test/fixtures/tokens/template-string/invalid-octal/input.js:1:2]
1 │ `\1`; 1 │ `\1`;

View file

@ -2,7 +2,6 @@ use std::path::{Path, PathBuf};
use oxc::{span::SourceType, transformer::BabelOptions}; use oxc::{span::SourceType, transformer::BabelOptions};
use serde::{de::DeserializeOwned, Deserialize}; use serde::{de::DeserializeOwned, Deserialize};
use serde_json::Value;
use crate::{ use crate::{
suite::{Case, Suite, TestResult}, suite::{Case, Suite, TestResult},
@ -167,15 +166,12 @@ impl Case for BabelCase {
fn skip_test_case(&self) -> bool { fn skip_test_case(&self) -> bool {
let not_supported_plugins = let not_supported_plugins =
["async-do-expression", "flow", "placeholders", "decorators-legacy", "recordAndTuple"]; ["async-do-expression", "flow", "placeholders", "decorators-legacy", "recordAndTuple"];
let has_not_supported_plugins = self.options.plugins.iter().any(|p| { let has_not_supported_plugins = self
let plugin_name = match p { .options
Value::String(plugin_name) => Some(plugin_name.as_str()), .plugins
Value::Array(a) => a.first().and_then(|plugin_name| plugin_name.as_str()), .unsupported
_ => None, .iter()
}; .any(|p| not_supported_plugins.iter().any(|plugin| plugin == p));
let plugin_name = plugin_name.expect("Failed to parse plugins config");
not_supported_plugins.contains(&plugin_name)
});
has_not_supported_plugins has_not_supported_plugins
|| self.options.allow_await_outside_function || self.options.allow_await_outside_function
|| self.options.allow_undeclared_exports || self.options.allow_undeclared_exports

View file

@ -1,6 +1,6 @@
commit: d20b314c commit: d20b314c
Passed: 376/1077 Passed: 377/1078
# All Passed: # All Passed:
* babel-plugin-transform-class-static-block * babel-plugin-transform-class-static-block
@ -1734,7 +1734,7 @@ rebuilt : ScopeId(1): []
x Output mismatch x Output mismatch
# babel-plugin-transform-typescript (39/152) # babel-plugin-transform-typescript (40/153)
* cast/as-expression/input.ts * cast/as-expression/input.ts
Unresolved references mismatch: Unresolved references mismatch:
after transform: ["T", "x"] after transform: ["T", "x"]

View file

@ -101,7 +101,10 @@ pub trait TestCase {
let options = self.options(); let options = self.options();
// Skip plugins we don't support yet // Skip plugins we don't support yet
if PLUGINS_NOT_SUPPORTED_YET.iter().any(|plugin| options.get_plugin(plugin).is_some()) { if PLUGINS_NOT_SUPPORTED_YET
.iter()
.any(|plugin| options.plugins.unsupported.iter().any(|p| plugin == p))
{
return true; return true;
} }
@ -118,14 +121,13 @@ pub trait TestCase {
} }
} }
// Legacy decorators is not supported by the parser // Legacy decorators is not supported
if options if options
.get_plugin("syntax-decorators") .plugins
.flatten() .proposal_decorators
.as_ref() .as_ref()
.and_then(|o| o.as_object()) .or(options.plugins.syntax_decorators.as_ref())
.and_then(|o| o.get("version")) .is_some_and(|o| o.version == "legacy")
.is_some_and(|s| s == "legacy")
{ {
return true; return true;
} }
@ -171,8 +173,8 @@ pub trait TestCase {
// Some babel test cases have a js extension, but contain typescript code. // Some babel test cases have a js extension, but contain typescript code.
// Therefore, if the typescript plugin exists, enable typescript. // Therefore, if the typescript plugin exists, enable typescript.
let source_type = SourceType::from_path(path).unwrap().with_typescript( let source_type = SourceType::from_path(path).unwrap().with_typescript(
self.options().get_plugin("transform-typescript").is_some() self.options().plugins.syntax_typescript.is_some()
|| self.options().get_plugin("syntax-typescript").is_some(), || self.options().plugins.typescript.is_some(),
); );
let driver = let driver =
@ -228,7 +230,7 @@ impl TestCase for ConformanceTestCase {
let mut source_type = SourceType::from_path(&self.path) let mut source_type = SourceType::from_path(&self.path)
.unwrap() .unwrap()
.with_script(true) .with_script(true)
.with_jsx(self.options.get_plugin("syntax-jsx").is_some()); .with_jsx(self.options.plugins.syntax_jsx);
source_type = match self.options.source_type.as_deref() { source_type = match self.options.source_type.as_deref() {
Some("unambiguous") => source_type.with_unambiguous(true), Some("unambiguous") => source_type.with_unambiguous(true),
@ -239,8 +241,8 @@ impl TestCase for ConformanceTestCase {
}; };
source_type = source_type.with_typescript( source_type = source_type.with_typescript(
self.options.get_plugin("transform-typescript").is_some() self.options.plugins.typescript.is_some()
|| self.options.get_plugin("syntax-typescript").is_some(), || self.options.plugins.syntax_typescript.is_some(),
); );
source_type source_type