diff --git a/.gitignore b/.gitignore index 569b67a04f7f3..e59b52e6f506c 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,6 @@ tasks/prettier_conformance/prettier/ # Ignore the failures directory, which is used to store the results of the codegen coverage tests tasks/coverage/failures/ + +# IntelliJ +.idea/ diff --git a/Cargo.lock b/Cargo.lock index e3df7fce47b25..e5da587211129 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1754,6 +1754,7 @@ name = "oxc_transformer" version = "0.10.0" dependencies = [ "bitflags 2.4.2", + "lazy_static", "oxc_allocator", "oxc_ast", "oxc_codegen", @@ -1763,6 +1764,7 @@ dependencies = [ "oxc_span", "oxc_syntax", "phf", + "regex-syntax 0.8.2", "rustc-hash", "serde", ] diff --git a/Cargo.toml b/Cargo.toml index 7ee1a96be5e46..f4d99a0aef59a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -111,6 +111,7 @@ project-root = { version = "0.2.2" } quote = { version = "1.0.35" } rayon = { version = "1.9.0" } regex = { version = "1.10.3" } +regex-syntax = { version = "0.8.2" } rustc-hash = { version = "1.1.0", default-features = false, features = ["std"] } ryu-js = { version = "1.0.1" } ropey = { version = "1.6.1" } diff --git a/crates/oxc_transformer/Cargo.toml b/crates/oxc_transformer/Cargo.toml index 4dd6a7bee6d48..b72272f96dede 100644 --- a/crates/oxc_transformer/Cargo.toml +++ b/crates/oxc_transformer/Cargo.toml @@ -30,6 +30,8 @@ rustc-hash = { workspace = true } serde = { workspace = true, features = ["derive"] } phf = { workspace = true, features = ["macros"] } +regex-syntax = { workspace = true } +lazy_static = { workspace = true } [dev-dependencies] oxc_parser = { workspace = true } diff --git a/crates/oxc_transformer/src/es2018/dotall_regex.rs b/crates/oxc_transformer/src/es2018/dotall_regex.rs new file mode 100644 index 0000000000000..faf658b79b50f --- /dev/null +++ b/crates/oxc_transformer/src/es2018/dotall_regex.rs @@ -0,0 +1,68 @@ +use std::rc::Rc; +use lazy_static::lazy_static; + +use oxc_ast::{ast::*, AstBuilder}; +use oxc_span::SPAN; +use regex_syntax::ast::parse::Parser; +use regex_syntax::ast::{Ast}; + +use crate::options::{TransformOptions, TransformTarget}; + +/// ES2018: Dotall Regular Expression +/// +/// References: +/// * +/// * +pub struct DotallRegex<'a> { + ast: Rc>, +} + +impl<'a> DotallRegex<'a> { + pub fn new(ast: Rc>, options: &TransformOptions) -> Option { + (options.target < TransformTarget::ES2018 || options.dotall_regex).then_some(Self { ast }) + } + + pub fn transform_expression(&self, expr: &mut Expression<'a>) { + let Expression::RegExpLiteral(literal) = expr else { + return; + }; + if !literal.regex.flags.contains(RegExpFlags::S) { + return; + } + literal.regex.flags.remove(RegExpFlags::S); + let mut parser = Parser::new(); + if let Ok(mut pattern) = parser.parse(literal.regex.pattern.as_str()) { + Self::transform_dot(&mut pattern); + let pattern = self.ast.new_str(pattern.to_string().as_str()); + let regex = self.ast.reg_exp_literal(SPAN, pattern, literal.regex.flags); + *expr = self.ast.literal_regexp_expression(regex); + } + } + + fn transform_dot(ast: &mut Ast) { + lazy_static! { + static ref DOTALL: Ast = { + let mut parser = Parser::new(); + parser.parse("[\\s\\S]").unwrap() + }; + } + match ast { + Ast::Dot(_) => { + *ast = DOTALL.clone(); + } + Ast::Concat(ref mut concat) => { + concat.asts.iter_mut().for_each(Self::transform_dot); + } + Ast::Group(ref mut group) => { + Self::transform_dot(&mut group.ast); + } + Ast::Alternation(ref mut alternation) => { + alternation.asts.iter_mut().for_each(Self::transform_dot); + } + Ast::Repetition(ref mut repetition) => { + Self::transform_dot(&mut repetition.ast); + } + _ => {} + } + } +} diff --git a/crates/oxc_transformer/src/es2018/mod.rs b/crates/oxc_transformer/src/es2018/mod.rs new file mode 100644 index 0000000000000..6ca3b1515fc25 --- /dev/null +++ b/crates/oxc_transformer/src/es2018/mod.rs @@ -0,0 +1,3 @@ +mod dotall_regex; + +pub use dotall_regex::DotallRegex; diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index 5d50d5e09305f..e324baaaf7fdc 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -10,6 +10,7 @@ mod context; mod es2015; mod es2016; +mod es2018; mod es2019; mod es2020; mod es2021; @@ -38,6 +39,7 @@ use crate::{ context::TransformerCtx, es2015::*, es2016::ExponentiationOperator, + es2018::DotallRegex, es2019::{JsonStrings, OptionalCatchBinding}, es2020::NullishCoalescingOperator, es2021::LogicalAssignmentOperators, @@ -74,6 +76,8 @@ pub struct Transformer<'a> { // es2019 es2019_json_strings: Option>, es2019_optional_catch_binding: Option>, + // es2018 + es2018_dotall_regex: Option>, // es2016 es2016_exponentiation_operator: Option>, // es2015 @@ -116,6 +120,8 @@ impl<'a> Transformer<'a> { // es2019 es2019_json_strings: JsonStrings::new(Rc::clone(&ast), &options), es2019_optional_catch_binding: OptionalCatchBinding::new(Rc::clone(&ast), &options), + // es2018 + es2018_dotall_regex: DotallRegex::new(Rc::clone(&ast), &options), // es2016 es2016_exponentiation_operator: ExponentiationOperator::new(Rc::clone(&ast), ctx.clone(), &options), // es2015 @@ -224,6 +230,7 @@ impl<'a> VisitMut<'a> for Transformer<'a> { fn visit_expression(&mut self, expr: &mut Expression<'a>) { // self.typescript.as_mut().map(|t| t.transform_expression(expr)); self.react_jsx.as_mut().map(|t| t.transform_expression(expr)); + self.es2018_dotall_regex.as_mut().map(|t| t.transform_expression(expr)); self.regexp_flags.as_mut().map(|t| t.transform_expression(expr)); self.es2021_logical_assignment_operators.as_mut().map(|t| t.transform_expression(expr)); diff --git a/crates/oxc_transformer/src/options.rs b/crates/oxc_transformer/src/options.rs index e658cefe3a74f..892af7149f43c 100644 --- a/crates/oxc_transformer/src/options.rs +++ b/crates/oxc_transformer/src/options.rs @@ -30,6 +30,7 @@ pub struct TransformOptions { pub function_name: bool, pub arrow_functions: Option, pub shorthand_properties: bool, + pub dotall_regex: bool, pub sticky_regex: bool, pub template_literals: bool, pub property_literals: bool, diff --git a/tasks/transform_conformance/babel.snap.md b/tasks/transform_conformance/babel.snap.md index 0f2bd8237d763..1aaf292694ad3 100644 --- a/tasks/transform_conformance/babel.snap.md +++ b/tasks/transform_conformance/babel.snap.md @@ -1,4 +1,4 @@ -Passed: 352/1369 +Passed: 354/1386 # All Passed: * babel-plugin-transform-numeric-separator @@ -695,9 +695,7 @@ Passed: 352/1369 * regression/gh-7388/input.js * regression/gh-8323/input.js -# babel-plugin-transform-dotall-regex (0/3) -* dotall-regex/simple/input.js -* dotall-regex/with-unicode-flag/input.js +# babel-plugin-transform-dotall-regex (2/3) * dotall-regex/with-unicode-property-escape/input.js # babel-plugin-transform-async-to-generator (1/43) @@ -832,7 +830,7 @@ Passed: 352/1369 * general/function-duplicate-name/input.js * general/object/input.js -# babel-plugin-transform-typescript (97/158) +# babel-plugin-transform-typescript (97/175) * class/abstract-class-decorated-method/input.ts * class/abstract-class-decorated-parameter/input.ts * class/accessor-allowDeclareFields-false/input.ts @@ -845,6 +843,23 @@ Passed: 352/1369 * class/parameter-properties-with-parameters/input.ts * class/private-method-override-transform-private/input.ts * class/transform-properties-declare-wrong-order/input.ts +* enum/boolean-value/input.ts +* enum/const/input.ts +* enum/constant-folding/input.ts +* enum/enum-merging-inner-references/input.ts +* enum/enum-merging-inner-references-shadow/input.ts +* enum/export/input.ts +* enum/inferred/input.ts +* enum/inner-references/input.ts +* enum/mix-references/input.ts +* enum/non-foldable-constant/input.ts +* enum/non-scoped/input.ts +* enum/outer-references/input.ts +* enum/scoped/input.ts +* enum/string-value/input.ts +* enum/string-value-template/input.ts +* enum/string-values-computed/input.ts +* enum/ts5.0-const-foldable/input.ts * exports/declared-types/input.ts * exports/export-const-enums/input.ts * exports/export-type-star-from/input.ts diff --git a/tasks/transform_conformance/src/test_case.rs b/tasks/transform_conformance/src/test_case.rs index a7528b0ea0ae4..7afd035155d03 100644 --- a/tasks/transform_conformance/src/test_case.rs +++ b/tasks/transform_conformance/src/test_case.rs @@ -119,6 +119,7 @@ pub trait TestCase { .get_plugin("transform-exponentiation-operator") .is_some(), shorthand_properties: options.get_plugin("transform-shorthand-properties").is_some(), + dotall_regex: options.get_plugin("transform-dotall-regex").is_some(), sticky_regex: options.get_plugin("transform-sticky-regex").is_some(), template_literals: options.get_plugin("transform-template-literals").is_some(), property_literals: options.get_plugin("transform-property-literals").is_some(),