feat(transfrom): transform-json-strings (#2168)

The pr intends to implement the plugin
`babel-plugin-transform-json-strings`. But here is only mutate
`Directive`, the `StringLiteral` is not implement. It need to changed
the `StringLiteral` printer.

I'm intend to add the raw of `StringLiteral`, it will be mutate at
plugin, and using the `raw` to print `StringLiteral`. If you other
ideas, please let me know.

---------

Co-authored-by: Boshen <boshenc@gmail.com>
This commit is contained in:
underfin 2024-01-25 18:39:02 +08:00 committed by GitHub
parent 8ca1812ad1
commit 2794064eef
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 91 additions and 13 deletions

View file

@ -46,9 +46,9 @@ language-tags = { workspace = true }
mime_guess = { workspace = true }
url = { workspace = true }
rust-lapper = "1.1.0"
once_cell = "1.19.0"
memchr = "2.7.1"
rust-lapper = "1.1.0"
once_cell = "1.19.0"
memchr = "2.7.1"
json-strip-comments = "1.0.1"
[dev-dependencies]

View file

@ -0,0 +1,56 @@
use oxc_ast::ast::*;
use oxc_span::Atom;
use oxc_syntax::identifier::{LS, PS};
use crate::options::{TransformOptions, TransformTarget};
/// ES2019: Json Strings
///
/// References:
/// * <https://babeljs.io/docs/babel-plugin-transform-json-strings>
/// * <https://github.com/babel/babel/tree/main/packages/babel-plugin-transform-json-strings>
pub struct JsonStrings;
impl JsonStrings {
pub fn new(options: &TransformOptions) -> Option<Self> {
(options.target < TransformTarget::ES2019 || options.json_strings).then(|| Self {})
}
// Allow `U+2028` and `U+2029` in string literals
// <https://tc39.es/proposal-json-superset>
// <https://github.com/tc39/proposal-json-superset>
fn normalize_str(str: &str) -> Option<Atom> {
if !str.contains(LS) && !str.contains(PS) {
return None;
}
let mut buf = String::new();
let mut is_escaped = false;
for c in str.chars() {
match (is_escaped, c) {
(false, LS) => buf.push_str("\\u2028"),
(false, PS) => buf.push_str("\\u2029"),
_ => buf.push(c),
}
is_escaped = !is_escaped && matches!(c, '\\');
}
Some(buf.into())
}
#[allow(clippy::unused_self)]
// TODO oxc_codegen currently prints json strings correctly,
// but we need a way to turn off this behaviour from codegen
// and do the transformation here.
pub fn transform_string_literal(&mut self, _literal: &mut StringLiteral) {
// let str = &self.ctx.semantic().source_text()[literal.span.start as usize + 1..literal.span.end as usize - 1];
// if let Some(value) = Self::normalize_str(str) {
// literal.value = value;
// }
}
#[allow(clippy::unused_self)]
pub fn transform_directive(&mut self, directive: &mut Directive) {
if let Some(value) = Self::normalize_str(directive.directive.as_str()) {
directive.directive = value;
}
}
}

View file

@ -1,3 +1,4 @@
mod json_strings;
mod optional_catch_binding;
pub use json_strings::JsonStrings;
pub use optional_catch_binding::OptionalCatchBinding;

View file

@ -35,10 +35,18 @@ use oxc_span::SourceType;
use proposals::Decorators;
use crate::{
context::TransformerCtx, es2015::*, es2016::ExponentiationOperator,
es2019::OptionalCatchBinding, es2020::NullishCoalescingOperator,
es2021::LogicalAssignmentOperators, es2022::ClassStaticBlock, es3::PropertyLiteral,
react_jsx::ReactJsx, regexp::RegexpFlags, typescript::TypeScript, utils::CreateVars,
context::TransformerCtx,
es2015::*,
es2016::ExponentiationOperator,
es2019::{JsonStrings, OptionalCatchBinding},
es2020::NullishCoalescingOperator,
es2021::LogicalAssignmentOperators,
es2022::ClassStaticBlock,
es3::PropertyLiteral,
react_jsx::ReactJsx,
regexp::RegexpFlags,
typescript::TypeScript,
utils::CreateVars,
};
pub use crate::{
@ -64,6 +72,7 @@ pub struct Transformer<'a> {
// es2020
es2020_nullish_coalescing_operators: Option<NullishCoalescingOperator<'a>>,
// es2019
es2019_json_strings: Option<JsonStrings>,
es2019_optional_catch_binding: Option<OptionalCatchBinding<'a>>,
// es2016
es2016_exponentiation_operator: Option<ExponentiationOperator<'a>>,
@ -105,6 +114,7 @@ impl<'a> Transformer<'a> {
// es2020
es2020_nullish_coalescing_operators: NullishCoalescingOperator::new(Rc::clone(&ast), ctx.clone(), &options),
// es2019
es2019_json_strings: JsonStrings::new(&options),
es2019_optional_catch_binding: OptionalCatchBinding::new(Rc::clone(&ast), &options),
// es2016
es2016_exponentiation_operator: ExponentiationOperator::new(Rc::clone(&ast), ctx.clone(), &options),
@ -291,4 +301,16 @@ impl<'a> VisitMut<'a> for Transformer<'a> {
self.leave_node(kind);
}
fn visit_directive(&mut self, directive: &mut Directive) {
self.es2019_json_strings
.as_mut()
.map(|t: &mut JsonStrings| t.transform_directive(directive));
}
fn visit_string_literal(&mut self, lit: &mut StringLiteral) {
self.es2019_json_strings
.as_mut()
.map(|t: &mut JsonStrings| t.transform_string_literal(lit));
}
}

View file

@ -22,6 +22,7 @@ pub struct TransformOptions {
pub nullish_coalescing_operator: Option<NullishCoalescingOperatorOptions>,
// es2019
pub optional_catch_binding: bool,
pub json_strings: bool,
// es2016
pub exponentiation_operator: bool,
// es2015

View file

@ -1,8 +1,9 @@
Passed: 327/1369
Passed: 329/1369
# All Passed:
* babel-plugin-transform-numeric-separator
* babel-plugin-transform-optional-catch-binding
* babel-plugin-transform-json-strings
* babel-plugin-transform-shorthand-properties
* babel-plugin-transform-sticky-regex
* babel-plugin-transform-instanceof
@ -607,10 +608,6 @@ Passed: 327/1369
* transparent-expr-wrappers/ts-as-member-expression/input.ts
* transparent-expr-wrappers/ts-parenthesized-expression-member-call/input.ts
# babel-plugin-transform-json-strings (2/4)
* json-strings/directive-line-separator/input.js
* json-strings/directive-paragraph-separator/input.js
# babel-plugin-transform-async-generator-functions (0/22)
* async-generators/class-method/input.js
* async-generators/class-private-method/input.js

View file

@ -111,6 +111,7 @@ pub trait TestCase {
nullish_coalescing_operator: options
.get_plugin("transform-nullish-coalescing-operator")
.map(get_options::<NullishCoalescingOperatorOptions>),
json_strings: options.get_plugin("transform-json-strings").is_some(),
optional_catch_binding: options
.get_plugin("transform-optional-catch-binding")
.is_some(),