Skip to content

Commit

Permalink
feat(transformer): RegexpFlags (#977)
Browse files Browse the repository at this point in the history

Co-authored-by: Boshen <[email protected]>
  • Loading branch information
magic-akari and Boshen authored Oct 11, 2023
1 parent eaa0c58 commit dc08c94
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 51 deletions.
2 changes: 0 additions & 2 deletions crates/oxc_transformer/src/es2015/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
mod shorthand_properties;
mod sticky_regex;

pub use shorthand_properties::ShorthandProperties;
pub use sticky_regex::StickyRegex;
43 changes: 0 additions & 43 deletions crates/oxc_transformer/src/es2015/sticky_regex.rs

This file was deleted.

11 changes: 8 additions & 3 deletions crates/oxc_transformer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ mod es2021;
mod es2022;
mod options;
mod react_jsx;
mod regexp;
mod typescript;

use oxc_allocator::Allocator;
use oxc_ast::{ast::*, AstBuilder, VisitMut};
use oxc_span::SourceType;
use regexp::RegexpFlags;
use std::rc::Rc;

use es2015::ShorthandProperties;
Expand All @@ -35,6 +37,7 @@ pub use crate::options::{
#[derive(Default)]
pub struct Transformer<'a> {
typescript: Option<TypeScript<'a>>,
regexp_flags: Option<RegexpFlags<'a>>,
react_jsx: Option<ReactJsx<'a>>,
// es2022
es2022_class_static_block: Option<es2022::ClassStaticBlock<'a>>,
Expand All @@ -46,7 +49,6 @@ pub struct Transformer<'a> {
es2016_exponentiation_operator: Option<ExponentiationOperator<'a>>,
// es2015
es2015_shorthand_properties: Option<ShorthandProperties<'a>>,
es2015_sticky_regex: Option<es2015::StickyRegex<'a>>,
}

impl<'a> Transformer<'a> {
Expand All @@ -64,6 +66,9 @@ impl<'a> Transformer<'a> {
if let Some(react_options) = options.react {
t.react_jsx.replace(ReactJsx::new(Rc::clone(&ast), react_options));
}

t.regexp_flags = RegexpFlags::new_with_transform_target(Rc::clone(&ast), options.target);

if options.target < TransformTarget::ES2022 {
t.es2022_class_static_block.replace(es2022::ClassStaticBlock::new(Rc::clone(&ast)));
}
Expand All @@ -79,7 +84,6 @@ impl<'a> Transformer<'a> {
}
if options.target < TransformTarget::ES2015 {
t.es2015_shorthand_properties.replace(ShorthandProperties::new(Rc::clone(&ast)));
t.es2015_sticky_regex.replace(es2015::StickyRegex::new(Rc::clone(&ast)));
}
t
}
Expand All @@ -93,9 +97,10 @@ 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.regexp_flags.as_mut().map(|t| t.transform_expression(expr));

self.es2021_logical_assignment_operators.as_mut().map(|t| t.transform_expression(expr));
self.es2016_exponentiation_operator.as_mut().map(|t| t.transform_expression(expr));
self.es2015_sticky_regex.as_mut().map(|t| t.transform_expression(expr));

self.visit_expression_match(expr);
}
Expand Down
2 changes: 2 additions & 0 deletions crates/oxc_transformer/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ pub enum TransformTarget {
ES5,
ES2015,
ES2016,
ES2018,
ES2019,
ES2021,
ES2022,
ES2024,
#[default]
ESNext,
}
Expand Down
3 changes: 3 additions & 0 deletions crates/oxc_transformer/src/regexp/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod regexp_flags;

pub use regexp_flags::RegexpFlags;
71 changes: 71 additions & 0 deletions crates/oxc_transformer/src/regexp/regexp_flags.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use oxc_ast::{ast::*, AstBuilder};
use oxc_span::{Atom, Span};

use std::rc::Rc;

use crate::TransformTarget;

/// Transforms unsupported regex flags into Regex constructors.
///
/// i.e. `/regex/flags` -> `new RegExp('regex', 'flags')`
///
/// * ES2024 [Unicode Sets v](https://babel.dev/docs/babel-plugin-transform-unicode-sets-regex)
/// * ES2022 [Match Indices d](https://github.com/tc39/proposal-regexp-match-indices)
/// * ES2018 [Dotall s](https://babel.dev/docs/babel-plugin-transform-dotall-regex)
/// * ES2015 [Unicode u](https://babel.dev/docs/babel-plugin-transform-unicode-regex)
/// * ES2015 [Sticky y](https://babel.dev/docs/babel-plugin-transform-sticky-regex)
pub struct RegexpFlags<'a> {
ast: Rc<AstBuilder<'a>>,
transform_flags: RegExpFlags,
}

impl<'a> RegexpFlags<'a> {
pub fn new_with_transform_target(
ast: Rc<AstBuilder<'a>>,
transform_target: TransformTarget,
) -> Option<Self> {
let transform_flags = Self::from_transform_target(transform_target);
(!transform_flags.is_empty()).then(|| Self { ast, transform_flags })
}

// `/regex/flags` -> `new RegExp('regex', 'flags')`
pub fn transform_expression(&self, expr: &mut Expression<'a>) {
let Expression::RegExpLiteral(literal) = expr else { return };
let regex = &literal.regex;
if regex.flags.intersection(self.transform_flags).is_empty() {
return;
}
let ident = IdentifierReference::new(Span::default(), Atom::from("RegExp"));
let callee = self.ast.identifier_expression(ident);
let pattern = self.ast.string_literal(Span::default(), Atom::from(regex.pattern.as_str()));
let flags = self.ast.string_literal(Span::default(), Atom::from(regex.flags.to_string()));
let pattern_literal = self.ast.literal_string_expression(pattern);
let flags_literal = self.ast.literal_string_expression(flags);
let mut arguments = self.ast.new_vec_with_capacity(2);
arguments.push(Argument::Expression(pattern_literal));
arguments.push(Argument::Expression(flags_literal));
*expr = self.ast.new_expression(Span::default(), callee, arguments, None);
}

fn from_transform_target(value: TransformTarget) -> RegExpFlags {
let mut flag = RegExpFlags::empty();
if value < TransformTarget::ES2015 {
flag |= RegExpFlags::Y;
flag |= RegExpFlags::U;
}
if value < TransformTarget::ES2018 {
flag |= RegExpFlags::S;
}
if value < TransformTarget::ES2022 {
flag |= RegExpFlags::D;
}
if value < TransformTarget::ES2024 {
flag |= RegExpFlags::V;
}
if value < TransformTarget::ESNext {
flag |= RegExpFlags::I;
flag |= RegExpFlags::M;
}
flag
}
}
19 changes: 18 additions & 1 deletion tasks/transform_conformance/babel.snap.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
Passed: 97/1080
Passed: 97/1091

# babel-plugin-transform-unicode-sets-regex
* Failed: basic/basic/input.js
* Failed: basic/string-properties/input.js
* Failed: transform-u/basic/input.js
* Failed: transform-u/string-properties/input.js

# babel-plugin-transform-class-properties
* Failed: assumption-constantSuper/complex-super-class/input.js
Expand Down Expand Up @@ -701,6 +707,11 @@ Passed: 97/1080
* Failed: regression/gh-7388/input.js
* Failed: regression/gh-8323/input.js

# babel-plugin-transform-dotall-regex
* Failed: dotall-regex/simple/input.js
* Failed: dotall-regex/with-unicode-flag/input.js
* Failed: dotall-regex/with-unicode-property-escape/input.js

# babel-plugin-transform-async-to-generator
* Failed: assumption-ignoreFunctionLength-true/basic/input.mjs
* Failed: assumption-ignoreFunctionLength-true/export-default-function/input.mjs
Expand Down Expand Up @@ -764,6 +775,12 @@ Passed: 97/1080
* Passed: sticky-regex/basic/input.js
* Passed: sticky-regex/ignore-non-sticky/input.js

# babel-plugin-transform-unicode-regex
* Failed: unicode-regex/basic/input.js
* Failed: unicode-regex/ignore-non-unicode/input.js
* Failed: unicode-regex/negated-set/input.js
* Failed: unicode-regex/slash/input.js

# babel-plugin-transform-typescript
* Failed: class/abstract-class-decorated/input.ts
* Failed: class/abstract-class-decorated-method/input.ts
Expand Down
5 changes: 3 additions & 2 deletions tasks/transform_conformance/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub fn babel(options: &BabelOptions) {

let cases = [
// ES2024
// [Regex] "babel-plugin-transform-unicode-sets-regex",
"babel-plugin-transform-unicode-sets-regex",
// ES2022
"babel-plugin-transform-class-properties",
"babel-plugin-transform-class-static-block",
Expand All @@ -48,7 +48,7 @@ pub fn babel(options: &BabelOptions) {
"babel-plugin-transform-async-generator-functions",
"babel-plugin-transform-object-rest-spread",
// [Regex] "babel-plugin-transform-unicode-property-regex",
// [Regex] "babel-plugin-transform-dotall-regex",
"babel-plugin-transform-dotall-regex",
// [Regex] "babel-plugin-transform-named-capturing-groups-regex",
// ES2017
"babel-plugin-transform-async-to-generator",
Expand All @@ -57,6 +57,7 @@ pub fn babel(options: &BabelOptions) {
// ES2015
"babel-plugin-transform-shorthand-properties",
"babel-plugin-transform-sticky-regex",
"babel-plugin-transform-unicode-regex",
// TypeScript
"babel-plugin-transform-typescript",
// React
Expand Down

0 comments on commit dc08c94

Please sign in to comment.