diff --git a/Cargo.lock b/Cargo.lock index 542e7f5d36465..6f5be95f34d9d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1955,6 +1955,7 @@ dependencies = [ "oxc_codegen", "oxc_diagnostics", "oxc_parser", + "oxc_regular_expression", "oxc_semantic", "oxc_span", "oxc_syntax", diff --git a/crates/oxc_transformer/Cargo.toml b/crates/oxc_transformer/Cargo.toml index d3d9d4442165b..1223d3d20c5aa 100644 --- a/crates/oxc_transformer/Cargo.toml +++ b/crates/oxc_transformer/Cargo.toml @@ -21,13 +21,14 @@ test = false doctest = false [dependencies] -oxc_ast = { workspace = true } -oxc_span = { workspace = true } -oxc_allocator = { workspace = true } -oxc_diagnostics = { workspace = true } -oxc_syntax = { workspace = true, features = ["to_js_string"] } -oxc_traverse = { workspace = true } -oxc_semantic = { workspace = true } +oxc_ast = { workspace = true } +oxc_span = { workspace = true } +oxc_allocator = { workspace = true } +oxc_diagnostics = { workspace = true } +oxc_syntax = { workspace = true, features = ["to_js_string"] } +oxc_traverse = { workspace = true } +oxc_semantic = { workspace = true } +oxc_regular_expression = { workspace = true } dashmap = { workspace = true } indexmap = { workspace = true } diff --git a/crates/oxc_transformer/src/env/data/babel.rs b/crates/oxc_transformer/src/env/data/babel.rs index fc368ca97bb0c..56362bb253915 100644 --- a/crates/oxc_transformer/src/env/data/babel.rs +++ b/crates/oxc_transformer/src/env/data/babel.rs @@ -8,10 +8,17 @@ use crate::env::{targets::version::Version, Versions}; fn features() -> &'static FxHashMap { static FEATURES: OnceLock> = OnceLock::new(); FEATURES.get_or_init(|| { - let map: FxHashMap> = + let mut map: FxHashMap> = serde_json::from_str(include_str!("./@babel/compat_data/data/plugins.json")) .expect("failed to parse json"); + map.extend( + serde_json::from_str::>>(include_str!( + "./esbuild/features.json" + )) + .expect("failed to parse json"), + ); + map.into_iter() .map(|(feature, mut versions)| { (feature, { diff --git a/crates/oxc_transformer/src/env/data/esbuild/features.json b/crates/oxc_transformer/src/env/data/esbuild/features.json new file mode 100644 index 0000000000000..be61049afa459 --- /dev/null +++ b/crates/oxc_transformer/src/env/data/esbuild/features.json @@ -0,0 +1,23 @@ +{ + "esbuild-regexp-lookbehind-assertions": { + "chrome": "62", + "deno": "1.0", + "edge": "79", + "firefox": "78", + "hermes": "0.7", + "ios": "16.4", + "node": "8.10", + "opera": "49", + "safari": "16.4" + }, + "esbuild-regexp-match-indices": { + "chrome": "90", + "deno": "1.8", + "edge": "90", + "firefox": "88", + "ios": "15.0", + "node": "16.0", + "opera": "76", + "safari": "15.0" + } +} diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index 2eea9bae4d1fd..e2fbd1ed01f11 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -21,6 +21,7 @@ mod es2019; mod es2020; mod es2021; mod react; +mod regexp; mod typescript; mod helpers { @@ -41,6 +42,7 @@ use oxc_diagnostics::OxcDiagnostic; use oxc_semantic::{ScopeTree, SymbolTable}; use oxc_span::{SourceType, SPAN}; use oxc_traverse::{traverse_mut, Traverse, TraverseCtx}; +use regexp::RegExp; pub use crate::{ compiler_assumptions::CompilerAssumptions, @@ -74,6 +76,7 @@ pub struct Transformer<'a> { x2_es2018: ES2018<'a>, x2_es2016: ES2016<'a>, x3_es2015: ES2015<'a>, + x4_regexp: RegExp<'a>, } impl<'a> Transformer<'a> { @@ -102,7 +105,8 @@ impl<'a> Transformer<'a> { x2_es2019: ES2019::new(options.es2019, Rc::clone(&ctx)), x2_es2018: ES2018::new(options.es2018, Rc::clone(&ctx)), x2_es2016: ES2016::new(options.es2016, Rc::clone(&ctx)), - x3_es2015: ES2015::new(options.es2015, ctx), + x3_es2015: ES2015::new(options.es2015, Rc::clone(&ctx)), + x4_regexp: RegExp::new(options.regexp, ctx), } } @@ -177,6 +181,7 @@ impl<'a> Traverse<'a> for Transformer<'a> { self.x2_es2018.enter_expression(expr, ctx); self.x2_es2016.enter_expression(expr, ctx); self.x3_es2015.enter_expression(expr, ctx); + self.x4_regexp.enter_expression(expr, ctx); } fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { diff --git a/crates/oxc_transformer/src/options/transformer.rs b/crates/oxc_transformer/src/options/transformer.rs index aaea129daf0d3..9a1e9f4f6d562 100644 --- a/crates/oxc_transformer/src/options/transformer.rs +++ b/crates/oxc_transformer/src/options/transformer.rs @@ -14,6 +14,7 @@ use crate::{ es2021::ES2021Options, options::babel::BabelOptions, react::ReactOptions, + regexp::RegExpOptions, typescript::TypeScriptOptions, }; @@ -38,6 +39,8 @@ pub struct TransformOptions { /// [preset-react](https://babeljs.io/docs/babel-preset-react) pub react: ReactOptions, + pub regexp: RegExpOptions, + pub es2015: ES2015Options, pub es2016: ES2016Options, @@ -60,6 +63,7 @@ impl TransformOptions { es2019: ES2019Options::from_targets_and_bugfixes(targets, bugfixes), es2020: ES2020Options::from_targets_and_bugfixes(targets, bugfixes), es2021: ES2021Options::from_targets_and_bugfixes(targets, bugfixes), + regexp: RegExpOptions::from_targets_and_bugfixes(targets, bugfixes), ..Default::default() } } @@ -215,6 +219,29 @@ impl TransformOptions { } }; + let regexp = transformer_options.regexp; + if !regexp.sticky_flag { + transformer_options.regexp.sticky_flag = options.has_plugin("transform-sticky-regex"); + } + if !regexp.unicode_flag { + transformer_options.regexp.unicode_flag = options.has_plugin("transform-unicode-regex"); + } + if !regexp.dot_all_flag { + transformer_options.regexp.dot_all_flag = options.has_plugin("transform-dotall-regex"); + } + if !regexp.named_capture_groups { + transformer_options.regexp.named_capture_groups = + options.has_plugin("transform-named-capturing-groups-regex"); + } + if !regexp.unicode_property_escapes { + transformer_options.regexp.unicode_property_escapes = + options.has_plugin("transform-unicode-property-regex"); + } + if !regexp.set_notation { + transformer_options.regexp.set_notation = + options.has_plugin("transform-unicode-sets-regex"); + } + transformer_options.assumptions = if options.assumptions.is_null() { CompilerAssumptions::default() } else { diff --git a/crates/oxc_transformer/src/regexp/mod.rs b/crates/oxc_transformer/src/regexp/mod.rs new file mode 100644 index 0000000000000..04ae98cfaef64 --- /dev/null +++ b/crates/oxc_transformer/src/regexp/mod.rs @@ -0,0 +1,218 @@ +//! RegExp Transformer +//! +//! This module supports various RegExp plugins to handle unsupported RegExp literal features. +//! When an unsupported feature is detected, these plugins convert the RegExp literal into +//! a `new RegExp()` constructor call to avoid syntax errors. +//! +//! Note: You will need to include a polyfill for the `RegExp` constructor in your code to have the correct runtime behavior. +//! +//! ### ES2015 +//! +//! #### Sticky flag (`y`) +//! - @babel/plugin-transform-sticky-regex: +//! +//! #### Unicode flag (`u`) +//! - @babel/plugin-transform-unicode-regex: +//! +//! ### ES2018 +//! +//! #### DotAll flag (`s`) +//! - @babel/plugin-transform-dotall-regex: +//! - Spec: ECMAScript 2018: +//! +//! #### Lookbehind assertions (`/(?<=x)/` and `/(?x)`) +//! - @babel/plugin-transform-named-capturing-groups-regex: +//! +//! #### Unicode property escapes (`\p{...}` and `\P{...}`) +//! - @babel/plugin-transform-unicode-property-regex: +//! +//! ### ES2022 +//! +//! #### Match indices flag (`d`) +//! - Implementation: Same as esbuild's handling +//! +//! ### ES2024 +//! +//! #### Set notation + properties of strings (`v`) +//! - @babel/plugin-transform-unicode-sets-regex: +//! - TC39 Proposal: + +mod options; + +use std::borrow::Cow; +use std::mem; + +pub use options::RegExpOptions; +use oxc_allocator::Box; +use oxc_allocator::Vec; +use oxc_ast::ast::*; +use oxc_regular_expression::ast::{ + CharacterClass, CharacterClassContents, LookAroundAssertionKind, Pattern, Term, +}; +use oxc_semantic::ReferenceFlags; +use oxc_span::Atom; +use oxc_traverse::{Traverse, TraverseCtx}; + +use crate::context::Ctx; + +pub struct RegExp<'a> { + _ctx: Ctx<'a>, + options: RegExpOptions, +} + +impl<'a> RegExp<'a> { + pub fn new(options: RegExpOptions, ctx: Ctx<'a>) -> Self { + Self { _ctx: ctx, options } + } +} + +impl<'a> Traverse<'a> for RegExp<'a> { + fn enter_expression( + &mut self, + expr: &mut Expression<'a>, + ctx: &mut oxc_traverse::TraverseCtx<'a>, + ) { + let Expression::RegExpLiteral(ref mut regexp) = expr else { + return; + }; + + if !self.has_unsupported_regular_expression_flags(regexp.regex.flags) + && self.requires_pattern_analysis() + { + match try_parse_pattern(regexp, ctx) { + Ok(pattern) => { + let is_unsupported = self.has_unsupported_regular_expression_pattern(&pattern); + regexp.regex.pattern = RegExpPattern::Pattern(pattern); + if !is_unsupported { + return; + } + } + Err(err) => { + regexp.regex.pattern = RegExpPattern::Invalid(err); + return; + } + } + }; + + let pattern_source: Cow<'_, str> = match ®exp.regex.pattern { + RegExpPattern::Raw(raw) | RegExpPattern::Invalid(raw) => Cow::Borrowed(raw), + RegExpPattern::Pattern(p) => Cow::Owned(p.to_string()), + }; + + let callee = { + let symbol_id = ctx.scopes().find_binding(ctx.current_scope_id(), "RegExp"); + let ident = ctx.create_reference_id( + regexp.span, + Atom::from("RegExp"), + symbol_id, + ReferenceFlags::read(), + ); + ctx.ast.expression_from_identifier_reference(ident) + }; + + let mut arguments = ctx.ast.vec_with_capacity(2); + arguments.push( + ctx.ast.argument_expression( + ctx.ast.expression_string_literal(regexp.span, pattern_source), + ), + ); + + let flags = regexp.regex.flags.to_string(); + let flags = + ctx.ast.argument_expression(ctx.ast.expression_string_literal(regexp.span, flags)); + arguments.push(flags); + + *expr = ctx.ast.expression_new( + regexp.span, + callee, + arguments, + None::, + ); + } +} + +impl<'a> RegExp<'a> { + fn requires_pattern_analysis(&self) -> bool { + self.options.named_capture_groups + || self.options.unicode_property_escapes + || self.options.look_behind_assertions + } + + /// Check if the regular expression contains any unsupported flags. + fn has_unsupported_regular_expression_flags(&self, flags: RegExpFlags) -> bool { + flags.iter().any(|f| match f { + RegExpFlags::S if self.options.dot_all_flag => true, + RegExpFlags::Y if self.options.sticky_flag => true, + RegExpFlags::U if self.options.unicode_flag => true, + RegExpFlags::D if self.options.match_indices => true, + RegExpFlags::V if self.options.set_notation => true, + _ => false, + }) + } + + /// Check if the regular expression contains any unsupported syntax. + /// + /// Based on parsed regular expression pattern. + fn has_unsupported_regular_expression_pattern(&self, pattern: &Pattern<'a>) -> bool { + let check_terms = |terms: &Vec<'a, Term>| { + terms.iter().any(|element| match element { + Term::CapturingGroup(_) if self.options.named_capture_groups => true, + Term::UnicodePropertyEscape(_) if self.options.unicode_property_escapes => true, + Term::CharacterClass(character_class) if self.options.unicode_property_escapes => { + has_unicode_property_escape_character_class(character_class) + } + Term::LookAroundAssertion(assertion) + if self.options.look_behind_assertions + && matches!( + assertion.kind, + LookAroundAssertionKind::Lookbehind + | LookAroundAssertionKind::NegativeLookbehind + ) => + { + true + } + _ => false, + }) + }; + + pattern.body.body.iter().any(|alternative| check_terms(&alternative.body)) + } +} + +fn has_unicode_property_escape_character_class(character_class: &CharacterClass) -> bool { + character_class.body.iter().any(|element| match element { + CharacterClassContents::UnicodePropertyEscape(_) => true, + CharacterClassContents::NestedCharacterClass(character_class) => { + has_unicode_property_escape_character_class(character_class) + } + _ => false, + }) +} + +fn try_parse_pattern<'a>( + literal: &mut RegExpLiteral<'a>, + ctx: &mut TraverseCtx<'a>, +) -> Result>, &'a str> { + // Take the ownership of the pattern + let regexp_pattern = mem::replace(&mut literal.regex.pattern, RegExpPattern::Raw("")); + + match regexp_pattern { + RegExpPattern::Raw(raw) => { + use oxc_regular_expression::{ParserOptions, PatternParser}; + let options = ParserOptions { + span_offset: literal.span.start + 1, // exclude `/` + unicode_mode: literal.regex.flags.contains(RegExpFlags::U) + || literal.regex.flags.contains(RegExpFlags::V), + unicode_sets_mode: literal.regex.flags.contains(RegExpFlags::V), + }; + PatternParser::new(ctx.ast.allocator, raw, options) + .parse() + .map_or_else(|_| Err(raw), |p| Ok(ctx.alloc(p))) + } + RegExpPattern::Pattern(pattern) => Ok(pattern), + RegExpPattern::Invalid(raw) => Err(raw), + } +} diff --git a/crates/oxc_transformer/src/regexp/options.rs b/crates/oxc_transformer/src/regexp/options.rs new file mode 100644 index 0000000000000..60690db4fc682 --- /dev/null +++ b/crates/oxc_transformer/src/regexp/options.rs @@ -0,0 +1,49 @@ +use crate::env::{can_enable_plugin, Versions}; + +#[derive(Default, Debug, Clone, Copy)] +pub struct RegExpOptions { + /// Enables plugin to transform the RegExp literal has `y` flag + pub sticky_flag: bool, + /// Enables plugin to transform the RegExp literal has `u` flag + pub unicode_flag: bool, + /// Enables plugin to transform the RegExp literal has `s` flag + pub dot_all_flag: bool, + /// Enables plugin to transform the RegExp literal has `(?<=)` or `(?x)` named capture groups + pub named_capture_groups: bool, + /// Enables plugin to transform the RegExp literal has `\p{}` and `\P{}` unicode property escapes + pub unicode_property_escapes: bool, + /// Enables plugin to transform `d` flag + pub match_indices: bool, + /// Enables plugin to transform the RegExp literal has `v` flag + pub set_notation: bool, +} + +impl RegExpOptions { + #[must_use] + pub fn from_targets_and_bugfixes(targets: Option<&Versions>, bugfixes: bool) -> Self { + Self { + sticky_flag: can_enable_plugin("transform-sticky-regex", targets, bugfixes), + unicode_flag: can_enable_plugin("transform-unicode-regex", targets, bugfixes), + dot_all_flag: can_enable_plugin("transform-dotall-regex", targets, bugfixes), + look_behind_assertions: can_enable_plugin( + "esbuild-regexp-lookbehind-assertions", + targets, + bugfixes, + ), + named_capture_groups: can_enable_plugin( + "transform-named-capturing-groups-regex", + targets, + bugfixes, + ), + unicode_property_escapes: can_enable_plugin( + "transform-unicode-property-regex", + targets, + bugfixes, + ), + match_indices: can_enable_plugin("esbuild-regexp-match-indices", targets, bugfixes), + set_notation: can_enable_plugin("transform-unicode-sets-regex", targets, bugfixes), + } + } +} diff --git a/tasks/coverage/semantic_typescript.snap b/tasks/coverage/semantic_typescript.snap index f53e398365eed..97dda5ce6e446 100644 --- a/tasks/coverage/semantic_typescript.snap +++ b/tasks/coverage/semantic_typescript.snap @@ -46,10 +46,10 @@ after transform: ScopeId(0): [ScopeId(1), ScopeId(8), ScopeId(9), ScopeId(14), S rebuilt : ScopeId(0): [ScopeId(1), ScopeId(8), ScopeId(13), ScopeId(19), ScopeId(22), ScopeId(23)] Symbol reference IDs mismatch: after transform: SymbolId(1): [ReferenceId(0), ReferenceId(20), ReferenceId(22), ReferenceId(26), ReferenceId(34), ReferenceId(46), ReferenceId(48), ReferenceId(49), ReferenceId(51), ReferenceId(53), ReferenceId(55), ReferenceId(56), ReferenceId(58), ReferenceId(60), ReferenceId(65), ReferenceId(67), ReferenceId(68), ReferenceId(70), ReferenceId(74), ReferenceId(76), ReferenceId(77), ReferenceId(78), ReferenceId(80), ReferenceId(81), ReferenceId(83), ReferenceId(86), ReferenceId(89), ReferenceId(91), ReferenceId(97)] -rebuilt : SymbolId(0): [ReferenceId(38), ReferenceId(39), ReferenceId(42), ReferenceId(44), ReferenceId(47), ReferenceId(53), ReferenceId(54), ReferenceId(56), ReferenceId(60), ReferenceId(62), ReferenceId(65), ReferenceId(68), ReferenceId(71), ReferenceId(73), ReferenceId(79)] +rebuilt : SymbolId(0): [ReferenceId(39), ReferenceId(40), ReferenceId(43), ReferenceId(45), ReferenceId(48), ReferenceId(54), ReferenceId(55), ReferenceId(57), ReferenceId(61), ReferenceId(63), ReferenceId(66), ReferenceId(69), ReferenceId(72), ReferenceId(74), ReferenceId(80)] Unresolved references mismatch: -after transform: ["Object", "true", "undefined"] -rebuilt : ["Object", "undefined"] +after transform: ["Object", "RegExp", "true", "undefined"] +rebuilt : ["Object", "RegExp", "undefined"] tasks/coverage/typescript/tests/cases/compiler/APISample_linter.ts semantic error: Bindings mismatch: @@ -1496,43 +1496,43 @@ after transform: ScopeId(1): ["Tany"] rebuilt : ScopeId(1): [] Reference symbol mismatch: after transform: ReferenceId(0): Some("a") -rebuilt : ReferenceId(0): None +rebuilt : ReferenceId(1): None Reference symbol mismatch: after transform: ReferenceId(1): Some("a") -rebuilt : ReferenceId(1): None +rebuilt : ReferenceId(2): None Reference symbol mismatch: after transform: ReferenceId(2): Some("a") -rebuilt : ReferenceId(2): None +rebuilt : ReferenceId(3): None Reference symbol mismatch: after transform: ReferenceId(3): Some("a") -rebuilt : ReferenceId(3): None +rebuilt : ReferenceId(4): None Reference symbol mismatch: after transform: ReferenceId(4): Some("a") -rebuilt : ReferenceId(4): None +rebuilt : ReferenceId(5): None Reference symbol mismatch: after transform: ReferenceId(5): Some("A") -rebuilt : ReferenceId(5): None +rebuilt : ReferenceId(6): None Reference symbol mismatch: after transform: ReferenceId(6): Some("A") -rebuilt : ReferenceId(6): None +rebuilt : ReferenceId(7): None Reference symbol mismatch: after transform: ReferenceId(7): Some("A") -rebuilt : ReferenceId(7): None +rebuilt : ReferenceId(8): None Reference symbol mismatch: after transform: ReferenceId(8): Some("A") -rebuilt : ReferenceId(8): None +rebuilt : ReferenceId(9): None Reference symbol mismatch: after transform: ReferenceId(9): Some("A") -rebuilt : ReferenceId(9): None +rebuilt : ReferenceId(10): None Reference symbol mismatch: after transform: ReferenceId(10): Some("A") -rebuilt : ReferenceId(10): None +rebuilt : ReferenceId(11): None Reference symbol mismatch: after transform: ReferenceId(11): Some("A") -rebuilt : ReferenceId(11): None +rebuilt : ReferenceId(12): None Unresolved references mismatch: -after transform: [] -rebuilt : ["A", "a"] +after transform: ["RegExp"] +rebuilt : ["A", "RegExp", "a"] tasks/coverage/typescript/tests/cases/compiler/castNewObjectBug.ts semantic error: Bindings mismatch: @@ -5108,8 +5108,8 @@ Reference symbol mismatch: after transform: ReferenceId(0): Some("require") rebuilt : ReferenceId(0): None Unresolved references mismatch: -after transform: ["Error", "JSON"] -rebuilt : ["Error", "JSON", "require"] +after transform: ["Error", "JSON", "RegExp"] +rebuilt : ["Error", "JSON", "RegExp", "require"] tasks/coverage/typescript/tests/cases/compiler/controlFlowUnionContainingTypeParameter1.ts semantic error: Bindings mismatch: @@ -31707,7 +31707,10 @@ rebuilt : ["undefined"] tasks/coverage/typescript/tests/cases/conformance/controlFlow/controlFlowDoWhileStatement.ts semantic error: Unresolved references mismatch: after transform: ["Function", "RegExp", "undefined"] -rebuilt : ["undefined"] +rebuilt : ["RegExp", "undefined"] +Unresolved reference IDs mismatch for "RegExp": +after transform: [ReferenceId(23), ReferenceId(33), ReferenceId(42), ReferenceId(43)] +rebuilt : [ReferenceId(29), ReferenceId(38)] tasks/coverage/typescript/tests/cases/conformance/controlFlow/controlFlowElementAccess2.ts semantic error: Bindings mismatch: @@ -31746,7 +31749,10 @@ rebuilt : ScopeId(0): [ScopeId(1), ScopeId(2)] tasks/coverage/typescript/tests/cases/conformance/controlFlow/controlFlowForInStatement.ts semantic error: Unresolved references mismatch: after transform: ["Function", "RegExp"] -rebuilt : [] +rebuilt : ["RegExp"] +Unresolved reference IDs mismatch for "RegExp": +after transform: [ReferenceId(0), ReferenceId(11)] +rebuilt : [ReferenceId(1)] tasks/coverage/typescript/tests/cases/conformance/controlFlow/controlFlowForInStatement2.ts semantic error: Bindings mismatch: @@ -31809,10 +31815,10 @@ after transform: ScopeId(12): ["T", "data"] rebuilt : ScopeId(12): ["data"] Symbol reference IDs mismatch: after transform: SymbolId(15): [ReferenceId(29), ReferenceId(30)] -rebuilt : SymbolId(11): [ReferenceId(23)] -Unresolved references mismatch: -after transform: ["Error", "JSON", "RegExp"] -rebuilt : ["Error", "JSON"] +rebuilt : SymbolId(11): [ReferenceId(24)] +Unresolved reference IDs mismatch for "RegExp": +after transform: [ReferenceId(0), ReferenceId(31)] +rebuilt : [ReferenceId(1)] tasks/coverage/typescript/tests/cases/conformance/controlFlow/controlFlowInOperator.ts semantic error: Bindings mismatch: @@ -31971,8 +31977,8 @@ Symbol reference IDs mismatch: after transform: SymbolId(0): [ReferenceId(0), ReferenceId(1), ReferenceId(5), ReferenceId(6), ReferenceId(16)] rebuilt : SymbolId(0): [ReferenceId(11)] Unresolved references mismatch: -after transform: ["Object", "RegExpExecArray", "getFooOrNull", "getStringOrNumberOrNull"] -rebuilt : ["getFooOrNull", "getStringOrNumberOrNull"] +after transform: ["Object", "RegExp", "RegExpExecArray", "getFooOrNull", "getStringOrNumberOrNull"] +rebuilt : ["RegExp", "getFooOrNull", "getStringOrNumberOrNull"] tasks/coverage/typescript/tests/cases/conformance/controlFlow/typeGuardsTypeParameters.ts semantic error: Bindings mismatch: @@ -35668,9 +35674,9 @@ rebuilt : ScopeId(0): ["a", "arr", "x"] Scope children mismatch: after transform: ScopeId(0): [ScopeId(1), ScopeId(3)] rebuilt : ScopeId(0): [ScopeId(1)] -Unresolved references mismatch: -after transform: ["RegExp"] -rebuilt : [] +Unresolved reference IDs mismatch for "RegExp": +after transform: [ReferenceId(0), ReferenceId(22)] +rebuilt : [ReferenceId(13)] tasks/coverage/typescript/tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithAnyAndEveryType.ts semantic error: Namespaces exporting non-const are not supported by Babel. Change to const or see: https://babeljs.io/docs/en/babel-plugin-transform-typescript @@ -42468,8 +42474,8 @@ Reference symbol mismatch: after transform: ReferenceId(1): Some("a") rebuilt : ReferenceId(1): None Unresolved references mismatch: -after transform: [] -rebuilt : ["a"] +after transform: ["RegExp"] +rebuilt : ["RegExp", "a"] tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Symbols/parserES5SymbolIndexer1.ts semantic error: Bindings mismatch: @@ -43194,7 +43200,7 @@ after transform: ScopeId(10): ["T", "x"] rebuilt : ScopeId(9): ["x"] Symbol reference IDs mismatch: after transform: SymbolId(2): [ReferenceId(2), ReferenceId(4), ReferenceId(5), ReferenceId(21), ReferenceId(42)] -rebuilt : SymbolId(1): [ReferenceId(20), ReferenceId(40)] +rebuilt : SymbolId(1): [ReferenceId(20), ReferenceId(41)] Symbol flags mismatch: after transform: SymbolId(7): SymbolFlags(Export | Class) rebuilt : SymbolId(6): SymbolFlags(Class) diff --git a/tasks/transform_conformance/oxc.snap.md b/tasks/transform_conformance/oxc.snap.md index 3d9ff3baed752..40ce7d8c8d547 100644 --- a/tasks/transform_conformance/oxc.snap.md +++ b/tasks/transform_conformance/oxc.snap.md @@ -1,11 +1,12 @@ commit: 3bcfee23 -Passed: 13/41 +Passed: 21/49 # All Passed: * babel-plugin-transform-nullish-coalescing-operator * babel-plugin-transform-optional-catch-binding * babel-preset-typescript +* esbuild-tests # babel-plugin-transform-arrow-functions (1/2) diff --git a/tasks/transform_conformance/src/constants.rs b/tasks/transform_conformance/src/constants.rs index 04660d3f6f9e6..9d9cb81564a59 100644 --- a/tasks/transform_conformance/src/constants.rs +++ b/tasks/transform_conformance/src/constants.rs @@ -56,6 +56,9 @@ pub(crate) const PLUGINS: &[&str] = &[ "babel-plugin-transform-react-jsx-development", // // Proposal // "babel-plugin-proposal-decorators", + + // Tests port from esbuild + "esbuild-tests", ]; pub(crate) const PLUGINS_NOT_SUPPORTED_YET: &[&str] = &[ diff --git a/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/all-regex-plugins-enabled-by-targets/input.js b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/all-regex-plugins-enabled-by-targets/input.js new file mode 100644 index 0000000000000..baef065c1caf0 --- /dev/null +++ b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/all-regex-plugins-enabled-by-targets/input.js @@ -0,0 +1,21 @@ +// ES2015 +// RegExpSticky +x1 = /./y +// RegExpUnicode +x2 = /./u +// ES2018 +// RegExpDotAllFlag +a1 = /a.b/s +// RegExpLookbehindAssertions +b1 = /(?b)/ +// RegExpUnicodePropertyEscapes +d1 = /\p{Emoji}/u +// ES2022 +// RegExpMatchIndices +f1 = /y/d +// ES2024 +// RegExpSetNotation +g1 = /[\p{White_Space}&&\p{ASCII}]/v \ No newline at end of file diff --git a/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/all-regex-plugins-enabled-by-targets/options.json b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/all-regex-plugins-enabled-by-targets/options.json new file mode 100644 index 0000000000000..ec3370903d9ee --- /dev/null +++ b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/all-regex-plugins-enabled-by-targets/options.json @@ -0,0 +1,9 @@ +{ + "presets": [ + ["env", { + "targets": { + "chrome": "40" + } + }] + ] +} diff --git a/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/all-regex-plugins-enabled-by-targets/output.js b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/all-regex-plugins-enabled-by-targets/output.js new file mode 100644 index 0000000000000..03c4967db48e5 --- /dev/null +++ b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/all-regex-plugins-enabled-by-targets/output.js @@ -0,0 +1,9 @@ +x1 = new RegExp(".", "y"); +x2 = new RegExp(".", "u"); +a1 = new RegExp("a.b", "s"); +b1 = new RegExp("(?b)", ""); +d1 = new RegExp("\\p{Emoji}", "u"); +f1 = new RegExp("y", "d"); +g1 = new RegExp("[\\p{White_Space}&&\\p{ASCII}]", "v"); \ No newline at end of file diff --git a/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/igm/input.js b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/igm/input.js new file mode 100644 index 0000000000000..c994ef95d528c --- /dev/null +++ b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/igm/input.js @@ -0,0 +1 @@ +a1 = /a.b/igm; \ No newline at end of file diff --git a/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/igm/options.json b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/igm/options.json new file mode 100644 index 0000000000000..c871da605e38c --- /dev/null +++ b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/igm/options.json @@ -0,0 +1,9 @@ +{ + "presets": [ + ["env", { + "targets": { + "chrome": "40" + } + }] + ] +} \ No newline at end of file diff --git a/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/igm/output.js b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/igm/output.js new file mode 100644 index 0000000000000..c994ef95d528c --- /dev/null +++ b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/igm/output.js @@ -0,0 +1 @@ +a1 = /a.b/igm; \ No newline at end of file diff --git a/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-dotall-regex/input.js b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-dotall-regex/input.js new file mode 100644 index 0000000000000..e529a74bf2ac9 --- /dev/null +++ b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-dotall-regex/input.js @@ -0,0 +1 @@ +a1 = /a.b/s \ No newline at end of file diff --git a/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-dotall-regex/options.json b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-dotall-regex/options.json new file mode 100644 index 0000000000000..7b742af2f201b --- /dev/null +++ b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-dotall-regex/options.json @@ -0,0 +1,5 @@ +{ + "plugins": [ + "transform-dotall-regex" + ] +} diff --git a/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-dotall-regex/output.js b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-dotall-regex/output.js new file mode 100644 index 0000000000000..15fdf4bdc57e9 --- /dev/null +++ b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-dotall-regex/output.js @@ -0,0 +1 @@ +a1 = new RegExp("a.b", "s"); \ No newline at end of file diff --git a/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-named-capturing-groups-regex/input.js b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-named-capturing-groups-regex/input.js new file mode 100644 index 0000000000000..b16d24951898b --- /dev/null +++ b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-named-capturing-groups-regex/input.js @@ -0,0 +1 @@ +c1 = /(?b)/ diff --git a/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-named-capturing-groups-regex/options.json b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-named-capturing-groups-regex/options.json new file mode 100644 index 0000000000000..aceea377f4339 --- /dev/null +++ b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-named-capturing-groups-regex/options.json @@ -0,0 +1,5 @@ +{ + "plugins": [ + "transform-named-capturing-groups-regex" + ] +} diff --git a/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-named-capturing-groups-regex/output.js b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-named-capturing-groups-regex/output.js new file mode 100644 index 0000000000000..8e12f01100f11 --- /dev/null +++ b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-named-capturing-groups-regex/output.js @@ -0,0 +1 @@ +c1 = new RegExp("(?b)", ""); \ No newline at end of file diff --git a/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-sticky-regex/input.js b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-sticky-regex/input.js new file mode 100644 index 0000000000000..5ef23ff8beb04 --- /dev/null +++ b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-sticky-regex/input.js @@ -0,0 +1 @@ +x1 = /./y \ No newline at end of file diff --git a/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-sticky-regex/options.json b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-sticky-regex/options.json new file mode 100644 index 0000000000000..2068006483508 --- /dev/null +++ b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-sticky-regex/options.json @@ -0,0 +1,5 @@ +{ + "plugins": [ + "transform-sticky-regex" + ] +} diff --git a/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-sticky-regex/output.js b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-sticky-regex/output.js new file mode 100644 index 0000000000000..f064f35ffb62f --- /dev/null +++ b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-sticky-regex/output.js @@ -0,0 +1 @@ +x1 = new RegExp(".", "y"); diff --git a/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-unicode-property-regex/input.js b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-unicode-property-regex/input.js new file mode 100644 index 0000000000000..06ab375088ef1 --- /dev/null +++ b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-unicode-property-regex/input.js @@ -0,0 +1 @@ +d1 = /\p{Emoji}/u \ No newline at end of file diff --git a/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-unicode-property-regex/options.json b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-unicode-property-regex/options.json new file mode 100644 index 0000000000000..73e373dc6f57f --- /dev/null +++ b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-unicode-property-regex/options.json @@ -0,0 +1,5 @@ +{ + "plugins": [ + "transform-unicode-property-regex" + ] +} diff --git a/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-unicode-property-regex/output.js b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-unicode-property-regex/output.js new file mode 100644 index 0000000000000..10074f71c555e --- /dev/null +++ b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-unicode-property-regex/output.js @@ -0,0 +1 @@ +d1 = new RegExp("\\p{Emoji}", "u"); \ No newline at end of file diff --git a/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-unicode-regex/input.js b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-unicode-regex/input.js new file mode 100644 index 0000000000000..eb14085e948a9 --- /dev/null +++ b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-unicode-regex/input.js @@ -0,0 +1 @@ +x2 = /./u \ No newline at end of file diff --git a/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-unicode-regex/options.json b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-unicode-regex/options.json new file mode 100644 index 0000000000000..7a0a4be32f40a --- /dev/null +++ b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-unicode-regex/options.json @@ -0,0 +1,5 @@ +{ + "plugins": [ + "transform-unicode-regex" + ] +} diff --git a/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-unicode-regex/output.js b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-unicode-regex/output.js new file mode 100644 index 0000000000000..5ccfa55e5afc1 --- /dev/null +++ b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-unicode-regex/output.js @@ -0,0 +1 @@ +x2 = new RegExp(".", "u"); diff --git a/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-unicode-sets-regex/input.js b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-unicode-sets-regex/input.js new file mode 100644 index 0000000000000..b3c27e4005464 --- /dev/null +++ b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-unicode-sets-regex/input.js @@ -0,0 +1 @@ +g1 = /[\p{White_Space}&&\p{ASCII}]/v diff --git a/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-unicode-sets-regex/options.json b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-unicode-sets-regex/options.json new file mode 100644 index 0000000000000..07115e0d5632d --- /dev/null +++ b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-unicode-sets-regex/options.json @@ -0,0 +1,5 @@ +{ + "plugins": [ + "transform-unicode-sets-regex" + ] +} diff --git a/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-unicode-sets-regex/output.js b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-unicode-sets-regex/output.js new file mode 100644 index 0000000000000..de413b03d2c08 --- /dev/null +++ b/tasks/transform_conformance/tests/esbuild-tests/test/fixtures/regexp/transform-unicode-sets-regex/output.js @@ -0,0 +1 @@ +g1 = new RegExp("[\\p{White_Space}&&\\p{ASCII}]", "v"); \ No newline at end of file