mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
feat(transformer): duplicate keys (#1649)
This commit is contained in:
parent
65c07728fc
commit
e331cc2677
7 changed files with 86 additions and 1 deletions
74
crates/oxc_transformer/src/es2015/duplicate_keys.rs
Normal file
74
crates/oxc_transformer/src/es2015/duplicate_keys.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
|
mod duplicate_keys;
|
||||||
mod function_name;
|
mod function_name;
|
||||||
mod shorthand_properties;
|
mod shorthand_properties;
|
||||||
mod template_literals;
|
mod template_literals;
|
||||||
|
|
||||||
|
pub use duplicate_keys::DuplicateKeys;
|
||||||
pub use function_name::FunctionName;
|
pub use function_name::FunctionName;
|
||||||
pub use shorthand_properties::ShorthandProperties;
|
pub use shorthand_properties::ShorthandProperties;
|
||||||
pub use template_literals::TemplateLiterals;
|
pub use template_literals::TemplateLiterals;
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ pub struct Transformer<'a> {
|
||||||
es2015_function_name: Option<FunctionName<'a>>,
|
es2015_function_name: Option<FunctionName<'a>>,
|
||||||
es2015_shorthand_properties: Option<ShorthandProperties<'a>>,
|
es2015_shorthand_properties: Option<ShorthandProperties<'a>>,
|
||||||
es2015_template_literals: Option<TemplateLiterals<'a>>,
|
es2015_template_literals: Option<TemplateLiterals<'a>>,
|
||||||
|
es2015_duplicate_keys: Option<DuplicateKeys<'a>>,
|
||||||
es3_property_literal: Option<PropertyLiteral<'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_function_name: FunctionName::new(Rc::clone(&ast), ctx.clone(), &options),
|
||||||
es2015_shorthand_properties: ShorthandProperties::new(Rc::clone(&ast), &options),
|
es2015_shorthand_properties: ShorthandProperties::new(Rc::clone(&ast), &options),
|
||||||
es2015_template_literals: TemplateLiterals::new(Rc::clone(&ast), &options),
|
es2015_template_literals: TemplateLiterals::new(Rc::clone(&ast), &options),
|
||||||
|
es2015_duplicate_keys: DuplicateKeys::new(Rc::clone(&ast), &options),
|
||||||
// other
|
// other
|
||||||
es3_property_literal: PropertyLiteral::new(Rc::clone(&ast), &options),
|
es3_property_literal: PropertyLiteral::new(Rc::clone(&ast), &options),
|
||||||
react_jsx: ReactJsx::new(Rc::clone(&ast), ctx.clone(), 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>) {
|
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_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() {
|
for property in expr.properties.iter_mut() {
|
||||||
self.visit_object_property_kind(property);
|
self.visit_object_property_kind(property);
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ pub struct TransformOptions {
|
||||||
// es2016
|
// es2016
|
||||||
pub exponentiation_operator: bool,
|
pub exponentiation_operator: bool,
|
||||||
// es2015
|
// es2015
|
||||||
|
pub duplicate_keys: bool,
|
||||||
pub function_name: bool,
|
pub function_name: bool,
|
||||||
pub shorthand_properties: bool,
|
pub shorthand_properties: bool,
|
||||||
pub sticky_regex: bool,
|
pub sticky_regex: bool,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
Passed: 270/1103
|
Passed: 277/1111
|
||||||
|
|
||||||
# All Passed:
|
# All Passed:
|
||||||
* babel-plugin-transform-numeric-separator
|
* babel-plugin-transform-numeric-separator
|
||||||
|
|
@ -772,6 +772,9 @@ Passed: 270/1103
|
||||||
* loose/ignoreToPrimitiveHint/input.js
|
* loose/ignoreToPrimitiveHint/input.js
|
||||||
* loose/mutableTemplateObject/input.js
|
* loose/mutableTemplateObject/input.js
|
||||||
|
|
||||||
|
# babel-plugin-transform-duplicate-keys (7/8)
|
||||||
|
* combination/dupes/input.js
|
||||||
|
|
||||||
# babel-plugin-transform-typescript (66/158)
|
# babel-plugin-transform-typescript (66/158)
|
||||||
* class/abstract-class-decorated/input.ts
|
* class/abstract-class-decorated/input.ts
|
||||||
* class/abstract-class-decorated-method/input.ts
|
* class/abstract-class-decorated-method/input.ts
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,7 @@ const CASES: &[&str] = &[
|
||||||
"babel-plugin-transform-sticky-regex",
|
"babel-plugin-transform-sticky-regex",
|
||||||
"babel-plugin-transform-unicode-regex",
|
"babel-plugin-transform-unicode-regex",
|
||||||
"babel-plugin-transform-template-literals",
|
"babel-plugin-transform-template-literals",
|
||||||
|
"babel-plugin-transform-duplicate-keys",
|
||||||
// ES3
|
// ES3
|
||||||
"babel-plugin-transform-property-literals",
|
"babel-plugin-transform-property-literals",
|
||||||
// TypeScript
|
// TypeScript
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,7 @@ pub trait TestCase {
|
||||||
sticky_regex: options.get_plugin("transform-sticky-regex").is_some(),
|
sticky_regex: options.get_plugin("transform-sticky-regex").is_some(),
|
||||||
template_literals: options.get_plugin("transform-template-literals").is_some(),
|
template_literals: options.get_plugin("transform-template-literals").is_some(),
|
||||||
property_literals: options.get_plugin("transform-property-literals").is_some(),
|
property_literals: options.get_plugin("transform-property-literals").is_some(),
|
||||||
|
duplicate_keys: options.get_plugin("transform-duplicate-keys").is_some(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue