Skip to content

Commit

Permalink
feat(transfrom): transform-json-strings (#2168)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
underfin and Boshen committed Jan 25, 2024
1 parent 8ca1812 commit 2794064
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 13 deletions.
6 changes: 3 additions & 3 deletions crates/oxc_linter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
56 changes: 56 additions & 0 deletions crates/oxc_transformer/src/es2019/json_strings.rs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
3 changes: 2 additions & 1 deletion crates/oxc_transformer/src/es2019/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod json_strings;
mod optional_catch_binding;

pub use json_strings::JsonStrings;
pub use optional_catch_binding::OptionalCatchBinding;
30 changes: 26 additions & 4 deletions crates/oxc_transformer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand All @@ -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>>,
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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));
}
}
1 change: 1 addition & 0 deletions crates/oxc_transformer/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 2 additions & 5 deletions tasks/transform_conformance/babel.snap.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions tasks/transform_conformance/src/test_case.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down

0 comments on commit 2794064

Please sign in to comment.