feat(transformer): duplicate keys (#1649)

This commit is contained in:
Ken-HH24 2023-12-10 18:07:32 +08:00 committed by GitHub
parent 65c07728fc
commit e331cc2677
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 86 additions and 1 deletions

View file

@ -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);
}
}
}
}
}

View file

@ -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;

View file

@ -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>>,
}
@ -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)
@ -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);

View file

@ -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,

View file

@ -1,4 +1,4 @@
Passed: 270/1103
Passed: 277/1111
# All Passed:
* babel-plugin-transform-numeric-separator
@ -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

View file

@ -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

View file

@ -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(),
}
}