mirror of
https://github.com/danbulant/oxc
synced 2026-05-25 04:42:10 +00:00
feat(transformer): RegexpFlags (#977)
Co-authored-by: Boshen <boshenc@gmail.com>
This commit is contained in:
parent
eaa0c58e24
commit
dc08c949a2
8 changed files with 105 additions and 51 deletions
|
|
@ -1,5 +1,3 @@
|
||||||
mod shorthand_properties;
|
mod shorthand_properties;
|
||||||
mod sticky_regex;
|
|
||||||
|
|
||||||
pub use shorthand_properties::ShorthandProperties;
|
pub use shorthand_properties::ShorthandProperties;
|
||||||
pub use sticky_regex::StickyRegex;
|
|
||||||
|
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
use oxc_ast::{ast::*, AstBuilder};
|
|
||||||
use oxc_span::{Atom, Span};
|
|
||||||
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
/// ES2015: Sticky Regex
|
|
||||||
///
|
|
||||||
/// References:
|
|
||||||
/// * <https://babel.dev/docs/babel-plugin-transform-sticky-regex>
|
|
||||||
/// * <https://github.com/babel/babel/blob/main/packages/babel-plugin-transform-sticky-regex>
|
|
||||||
pub struct StickyRegex<'a> {
|
|
||||||
ast: Rc<AstBuilder<'a>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> StickyRegex<'a> {
|
|
||||||
pub fn new(ast: Rc<AstBuilder<'a>>) -> Self {
|
|
||||||
Self { ast }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn transform_expression<'b>(&mut self, expr: &'b mut Expression<'a>) {
|
|
||||||
let Expression::RegExpLiteral(reg_literal) = expr else { return };
|
|
||||||
if !reg_literal.regex.flags.contains(RegExpFlags::Y) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let ident = IdentifierReference::new(Span::default(), Atom::from("RegExp"));
|
|
||||||
let callee = self.ast.identifier_expression(ident);
|
|
||||||
let pattern_literal = self
|
|
||||||
.ast
|
|
||||||
.string_literal(Span::default(), Atom::from(reg_literal.regex.pattern.as_str()));
|
|
||||||
let flags_literal = self
|
|
||||||
.ast
|
|
||||||
.string_literal(Span::default(), Atom::from(reg_literal.regex.flags.to_string()));
|
|
||||||
let pattern_literal = self.ast.literal_string_expression(pattern_literal);
|
|
||||||
let flags_literal = self.ast.literal_string_expression(flags_literal);
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -14,11 +14,13 @@ mod es2021;
|
||||||
mod es2022;
|
mod es2022;
|
||||||
mod options;
|
mod options;
|
||||||
mod react_jsx;
|
mod react_jsx;
|
||||||
|
mod regexp;
|
||||||
mod typescript;
|
mod typescript;
|
||||||
|
|
||||||
use oxc_allocator::Allocator;
|
use oxc_allocator::Allocator;
|
||||||
use oxc_ast::{ast::*, AstBuilder, VisitMut};
|
use oxc_ast::{ast::*, AstBuilder, VisitMut};
|
||||||
use oxc_span::SourceType;
|
use oxc_span::SourceType;
|
||||||
|
use regexp::RegexpFlags;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use es2015::ShorthandProperties;
|
use es2015::ShorthandProperties;
|
||||||
|
|
@ -35,6 +37,7 @@ pub use crate::options::{
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Transformer<'a> {
|
pub struct Transformer<'a> {
|
||||||
typescript: Option<TypeScript<'a>>,
|
typescript: Option<TypeScript<'a>>,
|
||||||
|
regexp_flags: Option<RegexpFlags<'a>>,
|
||||||
react_jsx: Option<ReactJsx<'a>>,
|
react_jsx: Option<ReactJsx<'a>>,
|
||||||
// es2022
|
// es2022
|
||||||
es2022_class_static_block: Option<es2022::ClassStaticBlock<'a>>,
|
es2022_class_static_block: Option<es2022::ClassStaticBlock<'a>>,
|
||||||
|
|
@ -46,7 +49,6 @@ pub struct Transformer<'a> {
|
||||||
es2016_exponentiation_operator: Option<ExponentiationOperator<'a>>,
|
es2016_exponentiation_operator: Option<ExponentiationOperator<'a>>,
|
||||||
// es2015
|
// es2015
|
||||||
es2015_shorthand_properties: Option<ShorthandProperties<'a>>,
|
es2015_shorthand_properties: Option<ShorthandProperties<'a>>,
|
||||||
es2015_sticky_regex: Option<es2015::StickyRegex<'a>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Transformer<'a> {
|
impl<'a> Transformer<'a> {
|
||||||
|
|
@ -64,6 +66,9 @@ impl<'a> Transformer<'a> {
|
||||||
if let Some(react_options) = options.react {
|
if let Some(react_options) = options.react {
|
||||||
t.react_jsx.replace(ReactJsx::new(Rc::clone(&ast), react_options));
|
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 {
|
if options.target < TransformTarget::ES2022 {
|
||||||
t.es2022_class_static_block.replace(es2022::ClassStaticBlock::new(Rc::clone(&ast)));
|
t.es2022_class_static_block.replace(es2022::ClassStaticBlock::new(Rc::clone(&ast)));
|
||||||
}
|
}
|
||||||
|
|
@ -79,7 +84,6 @@ impl<'a> Transformer<'a> {
|
||||||
}
|
}
|
||||||
if options.target < TransformTarget::ES2015 {
|
if options.target < TransformTarget::ES2015 {
|
||||||
t.es2015_shorthand_properties.replace(ShorthandProperties::new(Rc::clone(&ast)));
|
t.es2015_shorthand_properties.replace(ShorthandProperties::new(Rc::clone(&ast)));
|
||||||
t.es2015_sticky_regex.replace(es2015::StickyRegex::new(Rc::clone(&ast)));
|
|
||||||
}
|
}
|
||||||
t
|
t
|
||||||
}
|
}
|
||||||
|
|
@ -93,9 +97,10 @@ impl<'a> VisitMut<'a> for Transformer<'a> {
|
||||||
fn visit_expression(&mut self, expr: &mut Expression<'a>) {
|
fn visit_expression(&mut self, expr: &mut Expression<'a>) {
|
||||||
// self.typescript.as_mut().map(|t| t.transform_expression(expr));
|
// self.typescript.as_mut().map(|t| t.transform_expression(expr));
|
||||||
// self.react_jsx.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.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.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);
|
self.visit_expression_match(expr);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,11 @@ pub enum TransformTarget {
|
||||||
ES5,
|
ES5,
|
||||||
ES2015,
|
ES2015,
|
||||||
ES2016,
|
ES2016,
|
||||||
|
ES2018,
|
||||||
ES2019,
|
ES2019,
|
||||||
ES2021,
|
ES2021,
|
||||||
ES2022,
|
ES2022,
|
||||||
|
ES2024,
|
||||||
#[default]
|
#[default]
|
||||||
ESNext,
|
ESNext,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
3
crates/oxc_transformer/src/regexp/mod.rs
Normal file
3
crates/oxc_transformer/src/regexp/mod.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
mod regexp_flags;
|
||||||
|
|
||||||
|
pub use regexp_flags::RegexpFlags;
|
||||||
71
crates/oxc_transformer/src/regexp/regexp_flags.rs
Normal file
71
crates/oxc_transformer/src/regexp/regexp_flags.rs
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
# babel-plugin-transform-class-properties
|
||||||
* Failed: assumption-constantSuper/complex-super-class/input.js
|
* Failed: assumption-constantSuper/complex-super-class/input.js
|
||||||
|
|
@ -701,6 +707,11 @@ Passed: 97/1080
|
||||||
* Failed: regression/gh-7388/input.js
|
* Failed: regression/gh-7388/input.js
|
||||||
* Failed: regression/gh-8323/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
|
# babel-plugin-transform-async-to-generator
|
||||||
* Failed: assumption-ignoreFunctionLength-true/basic/input.mjs
|
* Failed: assumption-ignoreFunctionLength-true/basic/input.mjs
|
||||||
* Failed: assumption-ignoreFunctionLength-true/export-default-function/input.mjs
|
* Failed: assumption-ignoreFunctionLength-true/export-default-function/input.mjs
|
||||||
|
|
@ -764,6 +775,12 @@ Passed: 97/1080
|
||||||
* Passed: sticky-regex/basic/input.js
|
* Passed: sticky-regex/basic/input.js
|
||||||
* Passed: sticky-regex/ignore-non-sticky/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
|
# babel-plugin-transform-typescript
|
||||||
* Failed: class/abstract-class-decorated/input.ts
|
* Failed: class/abstract-class-decorated/input.ts
|
||||||
* Failed: class/abstract-class-decorated-method/input.ts
|
* Failed: class/abstract-class-decorated-method/input.ts
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ pub fn babel(options: &BabelOptions) {
|
||||||
|
|
||||||
let cases = [
|
let cases = [
|
||||||
// ES2024
|
// ES2024
|
||||||
// [Regex] "babel-plugin-transform-unicode-sets-regex",
|
"babel-plugin-transform-unicode-sets-regex",
|
||||||
// ES2022
|
// ES2022
|
||||||
"babel-plugin-transform-class-properties",
|
"babel-plugin-transform-class-properties",
|
||||||
"babel-plugin-transform-class-static-block",
|
"babel-plugin-transform-class-static-block",
|
||||||
|
|
@ -48,7 +48,7 @@ pub fn babel(options: &BabelOptions) {
|
||||||
"babel-plugin-transform-async-generator-functions",
|
"babel-plugin-transform-async-generator-functions",
|
||||||
"babel-plugin-transform-object-rest-spread",
|
"babel-plugin-transform-object-rest-spread",
|
||||||
// [Regex] "babel-plugin-transform-unicode-property-regex",
|
// [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",
|
// [Regex] "babel-plugin-transform-named-capturing-groups-regex",
|
||||||
// ES2017
|
// ES2017
|
||||||
"babel-plugin-transform-async-to-generator",
|
"babel-plugin-transform-async-to-generator",
|
||||||
|
|
@ -57,6 +57,7 @@ pub fn babel(options: &BabelOptions) {
|
||||||
// ES2015
|
// ES2015
|
||||||
"babel-plugin-transform-shorthand-properties",
|
"babel-plugin-transform-shorthand-properties",
|
||||||
"babel-plugin-transform-sticky-regex",
|
"babel-plugin-transform-sticky-regex",
|
||||||
|
"babel-plugin-transform-unicode-regex",
|
||||||
// TypeScript
|
// TypeScript
|
||||||
"babel-plugin-transform-typescript",
|
"babel-plugin-transform-typescript",
|
||||||
// React
|
// React
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue