From 52c20d633c9453357bc7e00cf79a3df42fce4c1d Mon Sep 17 00:00:00 2001 From: Boshen <1430279+Boshen@users.noreply.github.com> Date: Fri, 1 Nov 2024 05:44:57 +0000 Subject: [PATCH] refactor(transformer): deserialize `BabelOptions::plugins` (#7045) --- .../src/es2015/arrow_functions.rs | 2 +- crates/oxc_transformer/src/es2015/mod.rs | 4 +- .../oxc_transformer/src/options/babel/mod.rs | 252 ++++++++++++++---- crates/oxc_transformer/src/options/env.rs | 120 ++------- crates/oxc_transformer/src/options/mod.rs | 65 +---- tasks/coverage/snapshots/parser_babel.snap | 17 +- tasks/coverage/src/babel/mod.rs | 16 +- .../snapshots/babel.snap.md | 4 +- tasks/transform_conformance/src/test_case.rs | 26 +- 9 files changed, 287 insertions(+), 219 deletions(-) diff --git a/crates/oxc_transformer/src/es2015/arrow_functions.rs b/crates/oxc_transformer/src/es2015/arrow_functions.rs index a952fec321e7f..93302c07633e7 100644 --- a/crates/oxc_transformer/src/es2015/arrow_functions.rs +++ b/crates/oxc_transformer/src/es2015/arrow_functions.rs @@ -136,7 +136,7 @@ use oxc_syntax::{ }; use oxc_traverse::{Ancestor, BoundIdentifier, Traverse, TraverseCtx}; -#[derive(Debug, Default, Clone, Deserialize)] +#[derive(Debug, Default, Clone, Copy, Deserialize)] pub struct ArrowFunctionsOptions { /// 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. diff --git a/crates/oxc_transformer/src/es2015/mod.rs b/crates/oxc_transformer/src/es2015/mod.rs index 415285101300d..f25631d805757 100644 --- a/crates/oxc_transformer/src/es2015/mod.rs +++ b/crates/oxc_transformer/src/es2015/mod.rs @@ -17,9 +17,7 @@ pub struct ES2015<'a> { impl<'a> ES2015<'a> { pub fn new(options: ES2015Options) -> Self { Self { - arrow_functions: ArrowFunctions::new( - options.arrow_function.clone().unwrap_or_default(), - ), + arrow_functions: ArrowFunctions::new(options.arrow_function.unwrap_or_default()), options, } } diff --git a/crates/oxc_transformer/src/options/babel/mod.rs b/crates/oxc_transformer/src/options/babel/mod.rs index f6b0825551406..c782a362fbdc7 100644 --- a/crates/oxc_transformer/src/options/babel/mod.rs +++ b/crates/oxc_transformer/src/options/babel/mod.rs @@ -2,9 +2,14 @@ mod env; use std::path::{Path, PathBuf}; -use serde::Deserialize; +use serde::{de::DeserializeOwned, Deserialize}; use serde_json::Value; +use crate::{ + es2015::ArrowFunctionsOptions, es2018::ObjectRestSpreadOptions, es2022::ClassPropertiesOptions, + jsx::JsxOptions, TypeScriptOptions, +}; + pub use env::{BabelEnvOptions, Targets}; /// Babel options @@ -20,7 +25,7 @@ pub struct BabelOptions { // Plugin and Preset options #[serde(default)] - pub plugins: Vec, // Can be a string or an array + pub plugins: BabelPlugins, #[serde(default)] pub presets: Vec, // 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. /// # Panics pub fn from_test_path(path: &Path) -> Self { - let mut options_json: Option = None; + let mut babel_options: Option = None; + let mut plugins_json = None; + for path in path.ancestors().take(3) { let file = path.join("options.json"); if !file.exists() { continue; } - let file = std::fs::read_to_string(&file).unwrap(); - let new_json: Self = serde_json::from_str(&file).unwrap(); - if let Some(existing_json) = options_json.as_mut() { - if existing_json.source_type.is_none() { - if let Some(source_type) = new_json.source_type { - existing_json.source_type = Some(source_type); + + let content = std::fs::read_to_string(&file).unwrap(); + let mut new_value = serde_json::from_str::(&content).unwrap(); + + let new_plugins = new_value.as_object_mut().unwrap().remove("plugins"); + if plugins_json.is_none() { + plugins_json = new_plugins; + } + + let new_options: Self = serde_json::from_value::(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 let Some(throws) = new_json.throws { - existing_json.throws = Some(throws); + if existing_options.throws.is_none() { + if let Some(throws) = new_options.throws { + existing_options.throws = Some(throws); } } - if existing_json.plugins.is_empty() { - existing_json.plugins = new_json.plugins; - } - if existing_json.presets.is_empty() { - existing_json.presets = new_json.presets; + if existing_options.presets.is_empty() { + existing_options.presets = new_options.presets; } } 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::(plugins_json).unwrap(); + } + options } 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 { - self.plugins.iter().any(|v| { - 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 - }) + self.plugins.syntax_typescript.is_some() } pub fn is_typescript_definition(&self) -> bool { - self.plugins.iter().filter_map(Value::as_array).any(|p| { - 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 - }) + self.plugins.syntax_typescript.is_some_and(|o| o.dts) } 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") } - /// Returns - /// * `Some` if the plugin exists without a config - /// * `Some>` if the plugin exists with a config - /// * `None` if the plugin does not exist - pub fn get_plugin(&self, name: &str) -> Option> { - self.plugins.iter().find_map(|v| Self::get_value(v, name)) - } - pub fn get_preset(&self, name: &str) -> Option> { 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 { 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, + pub unsupported: Vec, + // syntax + pub syntax_typescript: Option, + pub syntax_jsx: bool, + // decorators + pub syntax_decorators: Option, + pub proposal_decorators: Option, + // ts + pub typescript: Option, + // jsx + pub react_jsx: Option, + pub react_jsx_dev: Option, + 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, + // ES2016 + pub exponentiation_operator: bool, + // ES2017 + pub async_to_generator: bool, + // ES2018 + pub object_rest_spread: Option, + 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, +} + +/// +#[derive(Debug, Deserialize)] +struct PluginPresetEntries(Vec); + +impl TryFrom for BabelPlugins { + type Error = String; + + fn try_from(entries: PluginPresetEntries) -> Result { + let mut p = BabelPlugins::default(); + for entry in entries.0 { + match entry.name() { + "typescript" | "syntax-typescript" => { + p.syntax_typescript = Some(entry.value::()?); + } + "jsx" | "syntax-jsx" => p.syntax_jsx = true, + "syntax-decorators" => { + p.syntax_decorators = Some(entry.value::()?); + } + "proposal-decorators" => { + p.proposal_decorators = Some(entry.value::()?); + } + "transform-typescript" => { + p.typescript = + entry.value::().map_err(|err| p.errors.push(err)).ok(); + } + "transform-react-jsx" => { + p.react_jsx = + entry.value::().map_err(|err| p.errors.push(err)).ok(); + } + "transform-react-jsx-development" => { + p.react_jsx_dev = + entry.value::().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::() + .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::() + .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::() + .inspect_err(|err| p.errors.push(err.to_string())) + .ok(); + } + s => p.unsupported.push(s.to_string()), + } + } + Ok(p) + } +} + +/// +#[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(self) -> Result { + match self { + Self::String(_) | Self::Vec1(_) => Ok(T::default()), + Self::Tuple(name, v) => { + serde_json::from_value::(v).map_err(|err| format!("{name}: {err}")) + } + } + } +} diff --git a/crates/oxc_transformer/src/options/env.rs b/crates/oxc_transformer/src/options/env.rs index 4c3be81506cbb..9fecaee2c7fcd 100644 --- a/crates/oxc_transformer/src/options/env.rs +++ b/crates/oxc_transformer/src/options/env.rs @@ -1,14 +1,8 @@ use oxc_diagnostics::{Error, OxcDiagnostic}; use crate::{ - es2015::{ArrowFunctionsOptions, ES2015Options}, - es2016::ES2016Options, - es2017::ES2017Options, - es2018::{ES2018Options, ObjectRestSpreadOptions}, - es2019::ES2019Options, - es2020::ES2020Options, - es2021::ES2021Options, - es2022::{ClassPropertiesOptions, ES2022Options}, + es2015::ES2015Options, es2016::ES2016Options, es2017::ES2017Options, es2018::ES2018Options, + es2019::ES2019Options, es2020::ES2020Options, es2021::ES2021Options, es2022::ES2022Options, regexp::RegExpOptions, }; @@ -146,117 +140,59 @@ impl TryFrom<&BabelOptions> for EnvOptions { .unwrap_or_default(); let regexp = RegExpOptions { - sticky_flag: env.regexp.sticky_flag || options.has_plugin("transform-sticky-regex"), - unicode_flag: env.regexp.unicode_flag || options.has_plugin("transform-unicode-regex"), - dot_all_flag: env.regexp.dot_all_flag || options.has_plugin("transform-dotall-regex"), - look_behind_assertions: env.regexp.look_behind_assertions, + sticky_flag: env.regexp.sticky_flag || options.plugins.sticky_flag, + unicode_flag: env.regexp.unicode_flag || options.plugins.unicode_flag, + dot_all_flag: env.regexp.dot_all_flag || options.plugins.dot_all_flag, + look_behind_assertions: env.regexp.look_behind_assertions + || options.plugins.look_behind_assertions, 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 - || options.has_plugin("transform-unicode-property-regex"), + || options.plugins.unicode_property_escapes, match_indices: env.regexp.match_indices, - set_notation: env.regexp.set_notation - || options.has_plugin("transform-unicode-sets-regex"), + set_notation: env.regexp.set_notation || options.plugins.set_notation, }; let es2015 = ES2015Options { - arrow_function: { - let plugin_name = "transform-arrow-functions"; - options - .get_plugin(plugin_name) - .map(|o| { - o.and_then(|options| { - serde_json::from_value::(options) - .inspect_err(|err| { - report_error(plugin_name, err, false, &mut errors); - }) - .ok() - }) - .unwrap_or_default() - }) - .or(env.es2015.arrow_function) - }, + arrow_function: options.plugins.arrow_function.or(env.es2015.arrow_function), }; let es2016 = ES2016Options { - exponentiation_operator: { - let plugin_name = "transform-exponentiation-operator"; - options.get_plugin(plugin_name).is_some() || env.es2016.exponentiation_operator - }, + exponentiation_operator: options.plugins.exponentiation_operator + || env.es2016.exponentiation_operator, }; let es2017 = ES2017Options { - async_to_generator: { - let plugin_name = "transform-async-to-generator"; - options.get_plugin(plugin_name).is_some() || env.es2017.async_to_generator - }, + async_to_generator: options.plugins.async_to_generator || env.es2017.async_to_generator, }; let es2018 = ES2018Options { - object_rest_spread: { - let plugin_name = "transform-object-rest-spread"; - options - .get_plugin(plugin_name) - .map(|o| { - o.and_then(|options| { - serde_json::from_value::(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 - }, + object_rest_spread: options + .plugins + .object_rest_spread + .or(env.es2018.object_rest_spread), + async_generator_functions: options.plugins.async_generator_functions + || env.es2018.async_generator_functions, }; let es2019 = ES2019Options { - optional_catch_binding: { - let plugin_name = "transform-optional-catch-binding"; - options.get_plugin(plugin_name).is_some() || env.es2019.optional_catch_binding - }, + optional_catch_binding: options.plugins.optional_catch_binding + || env.es2019.optional_catch_binding, }; let es2020 = ES2020Options { - nullish_coalescing_operator: { - let plugin_name = "transform-nullish-coalescing-operator"; - options.get_plugin(plugin_name).is_some() || env.es2020.nullish_coalescing_operator - }, + nullish_coalescing_operator: options.plugins.nullish_coalescing_operator + || env.es2020.nullish_coalescing_operator, }; let es2021 = ES2021Options { - logical_assignment_operators: { - let plugin_name = "transform-logical-assignment-operators"; - options.get_plugin(plugin_name).is_some() || env.es2021.logical_assignment_operators - }, + logical_assignment_operators: options.plugins.logical_assignment_operators + || env.es2021.logical_assignment_operators, }; let es2022 = ES2022Options { - class_static_block: { - let plugin_name = "transform-class-static-block"; - 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::(options) - .inspect_err(|err| { - report_error(plugin_name, err, false, &mut errors); - }) - .ok() - }) - .unwrap_or_default() - }) - .or(env.es2022.class_properties) - }, + class_static_block: options.plugins.class_static_block || env.es2022.class_static_block, + class_properties: options.plugins.class_properties.or(env.es2022.class_properties), }; if !errors.is_empty() { diff --git a/crates/oxc_transformer/src/options/mod.rs b/crates/oxc_transformer/src/options/mod.rs index d459752957d07..f1d8ae567c2df 100644 --- a/crates/oxc_transformer/src/options/mod.rs +++ b/crates/oxc_transformer/src/options/mod.rs @@ -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. fn try_from(options: &BabelOptions) -> Result { let mut errors = Vec::::new(); + errors.extend(options.plugins.errors.iter().map(|err| Error::msg(err.clone()))); let assumptions = if options.assumptions.is_null() { CompilerAssumptions::default() @@ -92,15 +93,7 @@ impl TryFrom<&BabelOptions> for TransformOptions { .unwrap_or_default() }) } else { - options.get_plugin("transform-typescript").and_then(|options| { - options - .map(|options| { - serde_json::from_value::(options) - .inspect_err(|err| report_error("typescript", err, false, &mut errors)) - .ok() - }) - .unwrap_or_default() - }) + options.plugins.typescript.clone() } .unwrap_or_default(); @@ -109,35 +102,19 @@ impl TryFrom<&BabelOptions> for TransformOptions { .inspect_err(|err| report_error("react", err, true, &mut errors)) .unwrap_or_default() } else { - let jsx_plugin_name = "transform-react-jsx"; - let jsx_dev_name = "transform-react-jsx-development"; - let has_jsx_plugin = options.has_plugin(jsx_plugin_name); - let mut react_options = if has_jsx_plugin { - options.get_plugin(jsx_plugin_name).and_then(|options| { - options.and_then(|options| { - serde_json::from_value::(options) - .inspect_err(|err| { - report_error(jsx_plugin_name, err, false, &mut errors); - }) - .ok() - }) - }) + let mut jsx_options = if let Some(options) = &options.plugins.react_jsx_dev { + options.clone() + } else if let Some(options) = &options.plugins.react_jsx { + options.clone() } else { - options.get_plugin(jsx_dev_name).and_then(|options| { - options.and_then(|options| { - serde_json::from_value::(options) - .inspect_err(|err| report_error(jsx_dev_name, err, false, &mut errors)) - .ok() - }) - }) - } - .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 + JsxOptions::default() + }; + jsx_options.development = options.plugins.react_jsx_dev.is_some(); + jsx_options.jsx_plugin = options.plugins.react_jsx.is_some(); + 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 }; 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}",) }; 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::(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::>().join("\n"); - assert!(err_message.contains("transform-react-jsx: unknown field `filter`")); -} diff --git a/tasks/coverage/snapshots/parser_babel.snap b/tasks/coverage/snapshots/parser_babel.snap index 9bfa9838fc4be..f6aa699cfb0a1 100644 --- a/tasks/coverage/snapshots/parser_babel.snap +++ b/tasks/coverage/snapshots/parser_babel.snap @@ -3,7 +3,7 @@ commit: d20b314c parser_babel Summary: AST Parsed : 2126/2136 (99.53%) 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/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 @@ -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/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/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/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 @@ -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 │
() => {} + ╰──── + × Expected `<` but found `EOF` ╭─[babel/packages/babel-parser/test/fixtures/jsx/errors/unclosed-jsx-element/input.js:2:1] 1 │
() => {} ╰──── + × 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 ╭─[babel/packages/babel-parser/test/fixtures/tokens/template-string/invalid-octal/input.js:1:2] 1 │ `\1`; diff --git a/tasks/coverage/src/babel/mod.rs b/tasks/coverage/src/babel/mod.rs index 98b9df25f27b0..31cd21250dfc5 100644 --- a/tasks/coverage/src/babel/mod.rs +++ b/tasks/coverage/src/babel/mod.rs @@ -2,7 +2,6 @@ use std::path::{Path, PathBuf}; use oxc::{span::SourceType, transformer::BabelOptions}; use serde::{de::DeserializeOwned, Deserialize}; -use serde_json::Value; use crate::{ suite::{Case, Suite, TestResult}, @@ -167,15 +166,12 @@ impl Case for BabelCase { fn skip_test_case(&self) -> bool { let not_supported_plugins = ["async-do-expression", "flow", "placeholders", "decorators-legacy", "recordAndTuple"]; - let has_not_supported_plugins = self.options.plugins.iter().any(|p| { - let plugin_name = match p { - Value::String(plugin_name) => Some(plugin_name.as_str()), - Value::Array(a) => a.first().and_then(|plugin_name| plugin_name.as_str()), - _ => None, - }; - let plugin_name = plugin_name.expect("Failed to parse plugins config"); - not_supported_plugins.contains(&plugin_name) - }); + let has_not_supported_plugins = self + .options + .plugins + .unsupported + .iter() + .any(|p| not_supported_plugins.iter().any(|plugin| plugin == p)); has_not_supported_plugins || self.options.allow_await_outside_function || self.options.allow_undeclared_exports diff --git a/tasks/transform_conformance/snapshots/babel.snap.md b/tasks/transform_conformance/snapshots/babel.snap.md index eb4084270dd70..a8b05ebe66484 100644 --- a/tasks/transform_conformance/snapshots/babel.snap.md +++ b/tasks/transform_conformance/snapshots/babel.snap.md @@ -1,6 +1,6 @@ commit: d20b314c -Passed: 376/1077 +Passed: 377/1078 # All Passed: * babel-plugin-transform-class-static-block @@ -1734,7 +1734,7 @@ rebuilt : ScopeId(1): [] x Output mismatch -# babel-plugin-transform-typescript (39/152) +# babel-plugin-transform-typescript (40/153) * cast/as-expression/input.ts Unresolved references mismatch: after transform: ["T", "x"] diff --git a/tasks/transform_conformance/src/test_case.rs b/tasks/transform_conformance/src/test_case.rs index 54f0a1c366b4c..f1c9f0fae6646 100644 --- a/tasks/transform_conformance/src/test_case.rs +++ b/tasks/transform_conformance/src/test_case.rs @@ -101,7 +101,10 @@ pub trait TestCase { let options = self.options(); // 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; } @@ -118,14 +121,13 @@ pub trait TestCase { } } - // Legacy decorators is not supported by the parser + // Legacy decorators is not supported if options - .get_plugin("syntax-decorators") - .flatten() + .plugins + .proposal_decorators .as_ref() - .and_then(|o| o.as_object()) - .and_then(|o| o.get("version")) - .is_some_and(|s| s == "legacy") + .or(options.plugins.syntax_decorators.as_ref()) + .is_some_and(|o| o.version == "legacy") { return true; } @@ -171,8 +173,8 @@ pub trait TestCase { // Some babel test cases have a js extension, but contain typescript code. // Therefore, if the typescript plugin exists, enable typescript. let source_type = SourceType::from_path(path).unwrap().with_typescript( - self.options().get_plugin("transform-typescript").is_some() - || self.options().get_plugin("syntax-typescript").is_some(), + self.options().plugins.syntax_typescript.is_some() + || self.options().plugins.typescript.is_some(), ); let driver = @@ -228,7 +230,7 @@ impl TestCase for ConformanceTestCase { let mut source_type = SourceType::from_path(&self.path) .unwrap() .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() { Some("unambiguous") => source_type.with_unambiguous(true), @@ -239,8 +241,8 @@ impl TestCase for ConformanceTestCase { }; source_type = source_type.with_typescript( - self.options.get_plugin("transform-typescript").is_some() - || self.options.get_plugin("syntax-typescript").is_some(), + self.options.plugins.typescript.is_some() + || self.options.plugins.syntax_typescript.is_some(), ); source_type