Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 35 additions & 11 deletions crates/oxc_transformer/src/plugins/tagged_template_transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,36 +132,60 @@ impl<'a, 'ctx> TaggedTemplateTransform<'a, 'ctx> {
///
/// Handle its fields as follows:
/// quasis:
/// - Create an array expression containing the raw string literals
/// - Call the helper function with the array expression
/// - Create an array expression containing the cooked string literals
/// - If cooked differs from raw, create a second array with raw strings
/// - Call the helper function with the array expression(s)
/// - Create a logical OR expression to cache the result in the binding
/// - Wrap the logical OR expression as the first argument
/// expressions:
/// - Add each expression as the remaining arguments
///
/// Final arguments: `(binding || (binding = babelHelpers.taggedTemplateLiteral([<...quasis>])), <...expressions>)`
/// Final arguments:
/// - `(binding || (binding = babelHelpers.taggedTemplateLiteral([<...cooked>])), <...expressions>)` when cooked == raw
/// - `(binding || (binding = babelHelpers.taggedTemplateLiteral([<...cooked>], [<...raw>])), <...expressions>)` when cooked != raw
fn transform_template_literal(
&self,
binding: &BoundIdentifier<'a>,
quasi: TemplateLiteral<'a>,
ctx: &mut TraverseCtx<'a>,
) -> ArenaVec<'a, Argument<'a>> {
// `([<...quasis>])` (e.g. `["first", "second", "third"]`)
let elements = ctx.ast.vec_from_iter(quasi.quasis.iter().map(|quasi| {
let string = ctx.ast.expression_string_literal(SPAN, quasi.value.raw, None);
ArrayExpressionElement::from(string)
// Check if we need to pass the raw array separately
let needs_raw_array = quasi.quasis.iter().any(|quasi| match &quasi.value.cooked {
None => true, // Invalid escape sequence - cooked is None
Some(cooked) => cooked.as_str() != quasi.value.raw.as_str(),
});

// Create cooked array: `[cooked0, cooked1, ...]`
// Use `void 0` for elements with invalid escape sequences (where cooked is None)
let cooked_elements = ctx.ast.vec_from_iter(quasi.quasis.iter().map(|quasi| {
let expr = match &quasi.value.cooked {
Some(cooked) => ctx.ast.expression_string_literal(SPAN, *cooked, None),
None => ctx.ast.void_0(SPAN),
};
ArrayExpressionElement::from(expr)
}));
let cooked_argument = Argument::from(ctx.ast.expression_array(SPAN, cooked_elements));

// Add raw array if needed: `[raw0, raw1, ...]`
let raws_argument = needs_raw_array.then(|| {
let elements = ctx.ast.vec_from_iter(quasi.quasis.iter().map(|quasi| {
let string = ctx.ast.expression_string_literal(SPAN, quasi.value.raw, None);
ArrayExpressionElement::from(string)
}));
Argument::from(ctx.ast.expression_array(SPAN, elements))
});

let template_arguments =
ctx.ast.vec1(Argument::from(ctx.ast.expression_array(SPAN, elements)));
ctx.ast.vec_from_iter(iter::once(cooked_argument).chain(raws_argument));

// `babelHelpers.taggedTemplateLiteral([<...quasis>])`
// `babelHelpers.taggedTemplateLiteral([<...cooked>], [<...raw>]?)`
let template_call =
self.ctx.helper_call_expr(Helper::TaggedTemplateLiteral, SPAN, template_arguments, ctx);
// `binding || (binding = babelHelpers.taggedTemplateLiteral([<...quasis>]))`
// `binding || (binding = babelHelpers.taggedTemplateLiteral([<...cooked>], [<...raw>]?))`
let template_call =
Argument::from(Self::create_logical_or_expression(binding, template_call, ctx));

// `(binding || (binding = babelHelpers.taggedTemplateLiteral([<...quasis>])), <...expressions>)`
// `(binding || (binding = babelHelpers.taggedTemplateLiteral([<...cooked>], [<...raw>]?)), <...expressions>)`
ctx.ast.vec_from_iter(
// Add the template expressions as the remaining arguments
iter::once(template_call).chain(quasi.expressions.into_iter().map(Argument::from)),
Expand Down
2 changes: 1 addition & 1 deletion tasks/transform_conformance/snapshots/oxc.snap.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
commit: c92c4919

Passed: 198/329
Passed: 200/331

# All Passed:
* babel-plugin-transform-class-static-block
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
foo`</script>\n`
bar`</script>\t`
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
var _templateObject;
var _templateObject2;
foo(_templateObject || (_templateObject = babelHelpers.taggedTemplateLiteral(["</script>\n"], ["</script>\\n"])));
bar(_templateObject2 || (_templateObject2 = babelHelpers.taggedTemplateLiteral(["</script>\t"], ["</script>\\t"])));
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
foo`</script>\u`
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
var _templateObject;
foo(_templateObject || (_templateObject = babelHelpers.taggedTemplateLiteral([void 0], ["</script>\\u"])));
Loading