From 41a5318ba68e688a216e8147e3b5972184552dc3 Mon Sep 17 00:00:00 2001 From: Boshen Date: Sun, 14 Apr 2024 16:38:51 +0800 Subject: [PATCH] fix(transform_conformance): fixes to improve conformance coveage --- crates/oxc_transformer/src/react/options.rs | 16 ++- tasks/transform_conformance/babel.snap.md | 42 +++--- tasks/transform_conformance/src/lib.rs | 10 +- tasks/transform_conformance/src/test_case.rs | 138 +++++++++++-------- 4 files changed, 123 insertions(+), 83 deletions(-) diff --git a/crates/oxc_transformer/src/react/options.rs b/crates/oxc_transformer/src/react/options.rs index 1860ba9139ae8c..50125bdf50f4d0 100644 --- a/crates/oxc_transformer/src/react/options.rs +++ b/crates/oxc_transformer/src/react/options.rs @@ -21,7 +21,7 @@ fn default_for_pragma_frag() -> Cow<'static, str> { } #[derive(Debug, Clone, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(default, rename_all = "camelCase")] pub struct ReactOptions { #[serde(skip)] pub jsx_plugin: bool, @@ -79,8 +79,16 @@ pub struct ReactOptions { /// Defaults to `React.Fragment`. #[serde(default = "default_for_pragma_frag")] pub pragma_frag: Cow<'static, str>, - // - // `useBuiltIns` and `useSpread` are deprecated in babel 8. + + /// `useBuiltIns` is deprecated in Babel 8. + /// + /// This value is used to skip Babel tests, and is not used in oxc. + pub use_built_ins: Option, + + /// `useSpread` is deprecated in Babel 8. + /// + /// This value is used to skip Babel tests, and is not used in oxc. + pub use_spread: Option, } impl Default for ReactOptions { @@ -96,6 +104,8 @@ impl Default for ReactOptions { import_source: default_for_import_source(), pragma: default_for_pragma(), pragma_frag: default_for_pragma_frag(), + use_built_ins: None, + use_spread: None, } } } diff --git a/tasks/transform_conformance/babel.snap.md b/tasks/transform_conformance/babel.snap.md index a4dcc142275c43..08bf0d234e1309 100644 --- a/tasks/transform_conformance/babel.snap.md +++ b/tasks/transform_conformance/babel.snap.md @@ -1,10 +1,10 @@ -Passed: 178/392 +Passed: 191/415 # All Passed: * babel-plugin-transform-react-jsx-source -# babel-preset-typescript (4/22) +# babel-preset-typescript (4/23) * flow-compat/js-invalid/input.js * flow-compat/js-valid/input.js * flow-compat/ts-invalid/input.ts @@ -13,6 +13,7 @@ Passed: 178/392 * flow-compat/tsx-valid/input.tsx * jsx-compat/js-valid/input.js * jsx-compat/ts-invalid/input.ts +* jsx-compat/ts-invalid-babel-7/input.ts * jsx-compat/tsx-valid/input.tsx * node-extensions/import-in-cts/input.cts * node-extensions/type-assertion-in-cts/input.cts @@ -24,13 +25,16 @@ Passed: 178/392 * opts/optimizeConstEnums/input.ts * opts/rewriteImportExtensions/input.ts -# babel-plugin-transform-typescript (59/158) +# babel-plugin-transform-typescript (68/168) * class/abstract-class-decorated/input.ts * class/abstract-class-decorated-method/input.ts * class/abstract-class-decorated-parameter/input.ts * class/accessor-allowDeclareFields-false/input.ts * class/accessor-allowDeclareFields-true/input.ts +* class/accessor-allowDeclareFields-true-babel-7/input.ts +* class/declare-not-enabled-babel-7/input.ts * class/decorated-declare-properties/input.ts +* class/field-not-initialized-babel-7/input.ts * class/parameter-properties/input.ts * class/parameter-properties-late-super/input.ts * class/parameter-properties-with-class/input.ts @@ -39,6 +43,7 @@ Passed: 178/392 * 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-with-declare-disabled-babel-7/input.ts * declarations/erased/input.ts * declarations/export-declare-enum/input.ts * declarations/nested-namespace/input.mjs @@ -50,7 +55,6 @@ Passed: 178/392 * exports/export-type-from/input.ts * exports/export-type-star-from/input.ts * exports/export=/input.ts -* exports/export=-to-cjs/input.ts * exports/imported-types/input.ts * exports/imported-types-only-remove-type-imports/input.ts * exports/issue-9916-3/input.ts @@ -76,7 +80,6 @@ Passed: 178/392 * imports/import-type-not-removed/input.ts * imports/import=-declaration/input.ts * imports/import=-module/input.ts -* imports/import=-module-to-cjs/input.ts * imports/parameter-decorators/input.ts * imports/property-signature/input.ts * imports/type-only-export-specifier-1/input.ts @@ -121,16 +124,16 @@ Passed: 178/392 * optimize-const-enums/merged-exported/input.ts * regression/10162/input.ts * regression/10338/input.ts -* regression/11061/input.mjs * regression/15768/input.ts * variable-declaration/non-null-in-optional-chain/input.ts -# babel-preset-react (2/13) +# babel-preset-react (2/14) * preset-options/development/input.js * preset-options/development-runtime-automatic/input.js * preset-options/development-runtime-automatic-windows/input.js * preset-options/development-windows/input.js * preset-options/empty-options/input.js +* preset-options/empty-options-babel-7/input.js * preset-options/runtime-automatic/input.js * preset-options/runtime-classic/input.js * preset-options/runtime-classic-pragma-no-frag/input.js @@ -138,8 +141,7 @@ Passed: 178/392 * 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 (96/156) -* autoImport/after-polyfills-compiled-to-cjs/input.mjs +# babel-plugin-transform-react-jsx (100/163) * autoImport/auto-import-react-source-type-module/input.js * autoImport/complicated-scope-module/input.js * autoImport/import-source-pragma/input.js @@ -153,7 +155,12 @@ Passed: 178/392 * pure/unset-pragma-comment-automatic-runtime/input.js * pure/unset-pragma-comment-classic-runtime/input.js * pure/unset-pragma-option-automatic-runtime/input.js +* react/adds-appropriate-newlines-when-using-spread-attribute-babel-7/input.js * react/arrow-functions/input.js +* react/assignment-babel-7/input.js +* react/avoids-spread-babel-7/input.js +* react/does-not-add-source-self-babel-7/input.mjs +* react/handle-spread-with-proto-babel-7/input.js * react/honor-custom-jsx-comment/input.js * react/honor-custom-jsx-comment-if-jsx-pragma-option-set/input.js * react/optimisation.react.constant-elements/input.js @@ -168,6 +175,9 @@ Passed: 178/392 * react/should-throw-error-namespaces-if-not-flag/input.js * react/should-warn-when-importSource-is-set/input.js * react/should-warn-when-importSource-pragma-is-set/input.js +* react/wraps-props-in-react-spread-for-first-spread-attributes-babel-7/input.js +* react/wraps-props-in-react-spread-for-last-spread-attributes-babel-7/input.js +* react/wraps-props-in-react-spread-for-middle-spread-attributes-babel-7/input.js * react-automatic/arrow-functions/input.js * react-automatic/concatenates-adjacent-string-literals/input.js * react-automatic/does-not-add-source-self-automatic/input.mjs @@ -182,6 +192,7 @@ Passed: 178/392 * react-automatic/should-disallow-valueless-key/input.js * react-automatic/should-disallow-xml-namespacing/input.js * react-automatic/should-escape-xhtml-jsxtext/input.js +* react-automatic/should-escape-xhtml-jsxtext-babel-7/input.js * react-automatic/should-handle-attributed-elements/input.js * react-automatic/should-have-correct-comma-in-nested-children/input.js * react-automatic/should-properly-handle-keys/input.js @@ -191,14 +202,9 @@ Passed: 178/392 * regression/issue-12478-automatic/input.js * regression/issue-12478-classic/input.js * regression/pragma-frag-set-default-classic-runtime/input.js -* removed-options/invalid-use-builtins-false/input.js -* removed-options/invalid-use-builtins-true/input.js -* removed-options/invalid-use-spread-false/input.js -* removed-options/invalid-use-spread-true/input.js +* runtime/defaults-to-classis-babel-7/input.js * runtime/invalid-runtime/input.js * runtime/pragma-runtime-classsic/input.js -* spread-transform/transform-to-babel-extend/input.js -* spread-transform/transform-to-object-assign/input.js # babel-plugin-transform-react-display-name (15/16) * display-name/nested/input.js @@ -207,12 +213,13 @@ Passed: 178/392 * react-source/arrow-function/input.js * react-source/disable-with-super/input.js -# babel-plugin-transform-react-jsx-development (0/23) +# babel-plugin-transform-react-jsx-development (0/27) * cross-platform/disallow-__self-as-jsx-attribute/input.js * cross-platform/disallow-__source-as-jsx-attribute/input.js * cross-platform/generated-jsx/input.js * linux/auto-import-dev/input.js * linux/classic-runtime/input.js +* linux/classic-runtime-babel-7/input.js * linux/fragments/input.js * linux/handle-fragments-with-key/input.js * linux/handle-nonstatic-children/input.js @@ -221,8 +228,10 @@ Passed: 178/392 * linux/source-and-self-defined/input.js * linux/within-derived-classes-constructor/input.js * linux/within-ts-module-block/input.ts +* linux/within-ts-module-block-babel-7/input.ts * windows/auto-import-dev-windows/input.js * windows/classic-runtime-windows/input.js +* windows/classic-runtime-windows-babel-7/input.js * windows/fragments-windows/input.js * windows/handle-fragments-with-key-windows/input.js * windows/handle-nonstatic-children-windows/input.js @@ -231,4 +240,5 @@ Passed: 178/392 * windows/source-and-self-defined-windows/input.js * windows/within-derived-classes-constructor-windows/input.js * windows/within-ts-module-block/input.ts +* windows/within-ts-module-block-babel-7/input.ts diff --git a/tasks/transform_conformance/src/lib.rs b/tasks/transform_conformance/src/lib.rs index d838bb29c35ff4..d36ee787666e7b 100644 --- a/tasks/transform_conformance/src/lib.rs +++ b/tasks/transform_conformance/src/lib.rs @@ -44,7 +44,7 @@ fn fixture_root() -> PathBuf { snap_root().join("fixtures") } -const CASES: &[&str] = &[ +const PLUGINS: &[&str] = &[ // // ES2024 // "babel-plugin-transform-unicode-sets-regex", // // ES2022 @@ -103,6 +103,9 @@ const CASES: &[&str] = &[ // "babel-plugin-proposal-decorators", ]; +pub(crate) const PLUGINS_NOT_SUPPORTED_YET: &[&str] = + &["transform-object-rest-spread", "transform-modules-commonjs"]; + const EXCLUDE_TESTS: &[&str] = &["babel-plugin-transform-typescript/test/fixtures/enum"]; const CONFORMANCE_SNAPSHOT: &str = "babel.snap.md"; @@ -155,7 +158,7 @@ impl TestRunner { let mut transform_files = IndexMap::>::new(); let mut exec_files = IndexMap::>::new(); - for case in CASES { + for case in PLUGINS { let root = root.join(case).join("test/fixtures"); let (mut transform_paths, mut exec_paths): (Vec, Vec) = WalkDir::new(root) @@ -171,8 +174,7 @@ impl TestRunner { if EXCLUDE_TESTS.iter().any(|p| path.to_string_lossy().contains(p)) { return None; } - TestCaseKind::from_path(path) - .filter(|test_case| !test_case.skip_test_case()) + TestCaseKind::new(path).filter(|test_case| !test_case.skip_test_case()) }) .partition(|p| matches!(p, TestCaseKind::Transform(_))); diff --git a/tasks/transform_conformance/src/test_case.rs b/tasks/transform_conformance/src/test_case.rs index c79224fb311544..26e9c77e7eac0f 100644 --- a/tasks/transform_conformance/src/test_case.rs +++ b/tasks/transform_conformance/src/test_case.rs @@ -17,7 +17,7 @@ use oxc_transformer::{ DecoratorsOptions, ReactOptions, TransformOptions, Transformer, TypeScriptOptions, }; -use crate::{fixture_root, root, TestRunnerEnv}; +use crate::{fixture_root, root, TestRunnerEnv, PLUGINS_NOT_SUPPORTED_YET}; #[derive(Debug)] pub enum TestCaseKind { @@ -26,14 +26,7 @@ pub enum TestCaseKind { } impl TestCaseKind { - pub fn test(&self, filter: bool) -> bool { - match self { - Self::Transform(test_case) => test_case.test(filter), - Self::Exec(test_case) => test_case.test(filter), - } - } - - pub fn from_path(path: &Path) -> Option { + pub fn new(path: &Path) -> Option { // in `exec` directory if path.parent().is_some_and(|path| path.file_name().is_some_and(|n| n == "exec")) && path.extension().is_some_and(|ext| VALID_EXTENSIONS.contains(&ext.to_str().unwrap())) @@ -70,61 +63,76 @@ impl TestCaseKind { Self::Exec(exec_case) => &exec_case.path, } } + + pub fn test(&self, filter: bool) -> bool { + match self { + Self::Transform(test_case) => test_case.test(filter), + Self::Exec(test_case) => test_case.test(filter), + } + } +} + +fn transform_options(options: &BabelOptions) -> TransformOptions { + fn get_options(value: Option) -> T { + value.and_then(|v| serde_json::from_value::(v).ok()).unwrap_or_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::).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::, + ); + + TransformOptions { + assumptions: serde_json::from_value(options.assumptions.clone()).unwrap_or_default(), + decorators: options + .get_plugin("proposal-decorators") + .map(get_options::) + .unwrap_or_default(), + typescript: options + .get_plugin("transform-typescript") + .map(get_options::) + .unwrap_or_default(), + react, + } } pub trait TestCase { - fn new>(path: P) -> Self; + fn new(path: &Path) -> Self; fn options(&self) -> &BabelOptions; + fn transform_options(&self) -> &TransformOptions; + fn test(&self, filtered: bool) -> bool; fn path(&self) -> &Path; - fn transform_options(&self) -> TransformOptions { - fn get_options(value: Option) -> T { - value.and_then(|v| serde_json::from_value::(v).ok()).unwrap_or_default() - } + fn skip_test_case(&self) -> bool { let options = self.options(); - 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::).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::, - ); - - TransformOptions { - assumptions: serde_json::from_value(options.assumptions.clone()).unwrap_or_default(), - decorators: options - .get_plugin("proposal-decorators") - .map(get_options::) - .unwrap_or_default(), - typescript: options - .get_plugin("transform-typescript") - .map(get_options::) - .unwrap_or_default(), - react, + // Skip plugins we don't support yet + if PLUGINS_NOT_SUPPORTED_YET.iter().any(|plugin| options.get_plugin(plugin).is_some()) { + return true; } - } - fn skip_test_case(&self) -> bool { - let options = self.options(); - - // Skip test cases that are not supported by babel 8 - if let Some(b) = options.babel_8_breaking { - return !b; + // 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; + } } // Legacy decorators is not supported by the parser @@ -165,7 +173,7 @@ pub trait TestCase { let transformed_program = allocator.alloc(ret.program); - let result = Transformer::new(&allocator, path, semantic, self.transform_options()) + let result = Transformer::new(&allocator, path, semantic, self.transform_options().clone()) .build(transformed_program); result.map(|()| { @@ -180,19 +188,24 @@ pub trait TestCase { pub struct ConformanceTestCase { path: PathBuf, options: BabelOptions, + transform_options: TransformOptions, } impl TestCase for ConformanceTestCase { - fn new>(path: P) -> Self { - let path = path.into(); + fn new(path: &Path) -> Self { let options = BabelOptions::from_path(path.parent().unwrap()); - Self { path, options } + let transform_options = transform_options(&options); + Self { path: path.to_path_buf(), options, transform_options } } fn options(&self) -> &BabelOptions { &self.options } + fn transform_options(&self) -> &TransformOptions { + &self.transform_options + } + fn path(&self) -> &Path { &self.path } @@ -306,6 +319,7 @@ impl TestCase for ConformanceTestCase { pub struct ExecTestCase { path: PathBuf, options: BabelOptions, + transform_options: TransformOptions, } impl ExecTestCase { @@ -339,18 +353,22 @@ impl ExecTestCase { } impl TestCase for ExecTestCase { + fn new(path: &Path) -> Self { + let options = BabelOptions::from_path(path.parent().unwrap()); + let transform_options = transform_options(&options); + Self { path: path.to_path_buf(), options, transform_options } + } + fn options(&self) -> &BabelOptions { &self.options } - fn path(&self) -> &Path { - &self.path + fn transform_options(&self) -> &TransformOptions { + &self.transform_options } - fn new>(path: P) -> Self { - let path = path.into(); - let options = BabelOptions::from_path(path.parent().unwrap()); - Self { path, options } + fn path(&self) -> &Path { + &self.path } fn test(&self, filtered: bool) -> bool {