Skip to content

Commit

Permalink
feat(transformer): duplicate keys (#1649)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ken-HH24 authored Dec 10, 2023
1 parent 65c0772 commit e331cc2
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 1 deletion.
74 changes: 74 additions & 0 deletions crates/oxc_transformer/src/es2015/duplicate_keys.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#![allow(clippy::similar_names)]

use std::{collections::HashSet, rc::Rc};

use oxc_ast::{ast::*, AstBuilder};
use oxc_span::{Atom, SPAN};

use crate::options::{TransformOptions, TransformTarget};

/// ES2015: Duplicate Keys
///
/// References:
/// * <https://babeljs.io/docs/babel-plugin-transform-duplicate-keys>
/// * <https://github.com/babel/babel/blob/main/packages/babel-plugin-transform-duplicate-keys>
pub struct DuplicateKeys<'a> {
ast: Rc<AstBuilder<'a>>,
}

impl<'a> DuplicateKeys<'a> {
pub fn new(ast: Rc<AstBuilder<'a>>, options: &TransformOptions) -> Option<Self> {
(options.target < TransformTarget::ES2015 || options.duplicate_keys).then(|| Self { ast })
}

pub fn transform_object_expression<'b>(&mut self, obj_expr: &'b mut ObjectExpression<'a>) {
let mut visited_data: HashSet<Atom> = HashSet::new();
let mut visited_getters: HashSet<Atom> = HashSet::new();
let mut visited_setters: HashSet<Atom> = HashSet::new();

for property in obj_expr.properties.iter_mut() {
let ObjectPropertyKind::ObjectProperty(obj_property) = property else {
continue;
};

if obj_property.computed {
continue;
}

if let Some(name) = &obj_property.key.static_name() {
let mut is_duplicate = false;

match obj_property.kind {
PropertyKind::Get => {
if visited_data.contains(name) || visited_getters.contains(name) {
is_duplicate = true;
}
visited_getters.insert(name.clone());
}
PropertyKind::Set => {
if visited_data.contains(name) || visited_setters.contains(name) {
is_duplicate = true;
}
visited_setters.insert(name.clone());
}
PropertyKind::Init => {
if visited_data.contains(name)
|| visited_setters.contains(name)
|| visited_getters.contains(name)
{
is_duplicate = true;
}
visited_data.insert(name.clone());
}
}

if is_duplicate {
obj_property.computed = true;
let string_literal = StringLiteral::new(SPAN, name.as_str().into());
let expr = self.ast.literal_string_expression(string_literal);
obj_property.key = PropertyKey::Expression(expr);
}
}
}
}
}
2 changes: 2 additions & 0 deletions crates/oxc_transformer/src/es2015/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
mod duplicate_keys;
mod function_name;
mod shorthand_properties;
mod template_literals;

pub use duplicate_keys::DuplicateKeys;
pub use function_name::FunctionName;
pub use shorthand_properties::ShorthandProperties;
pub use template_literals::TemplateLiterals;
3 changes: 3 additions & 0 deletions crates/oxc_transformer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ pub struct Transformer<'a> {
es2015_function_name: Option<FunctionName<'a>>,
es2015_shorthand_properties: Option<ShorthandProperties<'a>>,
es2015_template_literals: Option<TemplateLiterals<'a>>,
es2015_duplicate_keys: Option<DuplicateKeys<'a>>,
es3_property_literal: Option<PropertyLiteral<'a>>,
}

Expand Down Expand Up @@ -101,6 +102,7 @@ impl<'a> Transformer<'a> {
es2015_function_name: FunctionName::new(Rc::clone(&ast), ctx.clone(), &options),
es2015_shorthand_properties: ShorthandProperties::new(Rc::clone(&ast), &options),
es2015_template_literals: TemplateLiterals::new(Rc::clone(&ast), &options),
es2015_duplicate_keys: DuplicateKeys::new(Rc::clone(&ast), &options),
// other
es3_property_literal: PropertyLiteral::new(Rc::clone(&ast), &options),
react_jsx: ReactJsx::new(Rc::clone(&ast), ctx.clone(), options)
Expand Down Expand Up @@ -189,6 +191,7 @@ impl<'a> VisitMut<'a> for Transformer<'a> {

fn visit_object_expression(&mut self, expr: &mut ObjectExpression<'a>) {
self.es2015_function_name.as_mut().map(|t| t.transform_object_expression(expr));
self.es2015_duplicate_keys.as_mut().map(|t| t.transform_object_expression(expr));

for property in expr.properties.iter_mut() {
self.visit_object_property_kind(property);
Expand Down
1 change: 1 addition & 0 deletions crates/oxc_transformer/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub struct TransformOptions {
// es2016
pub exponentiation_operator: bool,
// es2015
pub duplicate_keys: bool,
pub function_name: bool,
pub shorthand_properties: bool,
pub sticky_regex: bool,
Expand Down
5 changes: 4 additions & 1 deletion tasks/transform_conformance/babel.snap.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Passed: 270/1103
Passed: 277/1111

# All Passed:
* babel-plugin-transform-numeric-separator
Expand Down Expand Up @@ -772,6 +772,9 @@ Passed: 270/1103
* loose/ignoreToPrimitiveHint/input.js
* loose/mutableTemplateObject/input.js

# babel-plugin-transform-duplicate-keys (7/8)
* combination/dupes/input.js

# babel-plugin-transform-typescript (66/158)
* class/abstract-class-decorated/input.ts
* class/abstract-class-decorated-method/input.ts
Expand Down
1 change: 1 addition & 0 deletions tasks/transform_conformance/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ const CASES: &[&str] = &[
"babel-plugin-transform-sticky-regex",
"babel-plugin-transform-unicode-regex",
"babel-plugin-transform-template-literals",
"babel-plugin-transform-duplicate-keys",
// ES3
"babel-plugin-transform-property-literals",
// TypeScript
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 {
sticky_regex: options.get_plugin("transform-sticky-regex").is_some(),
template_literals: options.get_plugin("transform-template-literals").is_some(),
property_literals: options.get_plugin("transform-property-literals").is_some(),
duplicate_keys: options.get_plugin("transform-duplicate-keys").is_some(),
}
}

Expand Down

0 comments on commit e331cc2

Please sign in to comment.