mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 20:32:10 +00:00
feat(transformer): Shorthand Properties (#960)
This commit is contained in:
parent
817f8c295e
commit
21066a9ad7
7 changed files with 106 additions and 3 deletions
|
|
@ -420,6 +420,13 @@ impl<'a> PropertyKey<'a> {
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_specific_string_literal(&self, string: &str) -> bool {
|
||||||
|
match self {
|
||||||
|
PropertyKey::Expression(expr) => expr.is_specific_string_literal(string),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
|
|
||||||
3
crates/oxc_transformer/src/es2015/mod.rs
Normal file
3
crates/oxc_transformer/src/es2015/mod.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
mod shorthand_properties;
|
||||||
|
|
||||||
|
pub use shorthand_properties::ShorthandProperties;
|
||||||
64
crates/oxc_transformer/src/es2015/shorthand_properties.rs
Normal file
64
crates/oxc_transformer/src/es2015/shorthand_properties.rs
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
use oxc_ast::{ast::*, AstBuilder};
|
||||||
|
use oxc_span::GetSpan;
|
||||||
|
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
/// ES2015: Shorthand Properties
|
||||||
|
///
|
||||||
|
/// References:
|
||||||
|
/// * <https://babel.dev/docs/babel-plugin-transform-shorthand-properties>
|
||||||
|
/// * <https://github.com/babel/babel/blob/main/packages/babel-plugin-transform-shorthand-properties>
|
||||||
|
pub struct ShorthandProperties<'a> {
|
||||||
|
ast: Rc<AstBuilder<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ShorthandProperties<'a> {
|
||||||
|
pub fn new(ast: Rc<AstBuilder<'a>>) -> Self {
|
||||||
|
Self { ast }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn transform_object_property<'b>(&mut self, obj_prop: &'b mut ObjectProperty<'a>) {
|
||||||
|
if !obj_prop.shorthand && !obj_prop.method {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj_prop.shorthand = false;
|
||||||
|
obj_prop.method = false;
|
||||||
|
|
||||||
|
if obj_prop.computed {
|
||||||
|
// all computed key can never be transformed to `__proto__` setter unexpectedly
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We should handle the edge case of `__proto__` property.
|
||||||
|
// All shorthand properties with key `__proto__` can never be `__proto__` setter.
|
||||||
|
// But the transformed result can be `__proto__` setter unexpectedly.
|
||||||
|
// It's easy to fix it by using computed property.
|
||||||
|
|
||||||
|
let is_proto_string = obj_prop.key.is_specific_string_literal("__proto__");
|
||||||
|
|
||||||
|
if !is_proto_string && !obj_prop.key.is_specific_id("__proto__") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// We reach here, it means that the key is `__proto__` or `"__proto__"`.
|
||||||
|
|
||||||
|
// Transform `__proto__`/`"__proto__"` to computed property.
|
||||||
|
obj_prop.computed = true;
|
||||||
|
|
||||||
|
if is_proto_string {
|
||||||
|
// After the transformation, the string literal `"__proto__"` is already expected result.
|
||||||
|
//
|
||||||
|
// input:
|
||||||
|
// "__proto__"() {}
|
||||||
|
// output:
|
||||||
|
// ["__proto__"]: function() {}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert `[__proto__]` to `["__proto__"]`
|
||||||
|
|
||||||
|
let proto = StringLiteral { span: obj_prop.key.span(), value: "__proto__".into() };
|
||||||
|
let expr = self.ast.literal_string_expression(proto);
|
||||||
|
obj_prop.key = PropertyKey::Expression(expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
//! * <https://babel.dev/docs/presets>
|
//! * <https://babel.dev/docs/presets>
|
||||||
//! * <https://github.com/microsoft/TypeScript/blob/main/src/compiler/transformer.ts>
|
//! * <https://github.com/microsoft/TypeScript/blob/main/src/compiler/transformer.ts>
|
||||||
|
|
||||||
|
mod es2015;
|
||||||
mod es2016;
|
mod es2016;
|
||||||
mod es2019;
|
mod es2019;
|
||||||
mod es2021;
|
mod es2021;
|
||||||
|
|
@ -19,6 +20,7 @@ use oxc_ast::{ast::*, AstBuilder, VisitMut};
|
||||||
use oxc_span::SourceType;
|
use oxc_span::SourceType;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use es2015::ShorthandProperties;
|
||||||
use es2016::ExponentiationOperator;
|
use es2016::ExponentiationOperator;
|
||||||
use es2019::OptionalCatchBinding;
|
use es2019::OptionalCatchBinding;
|
||||||
use es2021::LogicalAssignmentOperators;
|
use es2021::LogicalAssignmentOperators;
|
||||||
|
|
@ -39,6 +41,8 @@ pub struct Transformer<'a> {
|
||||||
es2019_optional_catch_binding: Option<OptionalCatchBinding<'a>>,
|
es2019_optional_catch_binding: Option<OptionalCatchBinding<'a>>,
|
||||||
// es2016
|
// es2016
|
||||||
es2016_exponentiation_operator: Option<ExponentiationOperator<'a>>,
|
es2016_exponentiation_operator: Option<ExponentiationOperator<'a>>,
|
||||||
|
// es2015
|
||||||
|
es2015_shorthand_properties: Option<ShorthandProperties<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Transformer<'a> {
|
impl<'a> Transformer<'a> {
|
||||||
|
|
@ -66,6 +70,9 @@ impl<'a> Transformer<'a> {
|
||||||
if options.target < TransformTarget::ES2016 {
|
if options.target < TransformTarget::ES2016 {
|
||||||
t.es2016_exponentiation_operator.replace(ExponentiationOperator::new(Rc::clone(&ast)));
|
t.es2016_exponentiation_operator.replace(ExponentiationOperator::new(Rc::clone(&ast)));
|
||||||
}
|
}
|
||||||
|
if options.target < TransformTarget::ES2015 {
|
||||||
|
t.es2015_shorthand_properties.replace(ShorthandProperties::new(Rc::clone(&ast)));
|
||||||
|
}
|
||||||
t
|
t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,4 +99,14 @@ impl<'a, 'b> VisitMut<'a, 'b> for Transformer<'a> {
|
||||||
}
|
}
|
||||||
self.visit_statements(&mut clause.body.body);
|
self.visit_statements(&mut clause.body.body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn visit_object_property(&mut self, prop: &'b mut ObjectProperty<'a>) {
|
||||||
|
self.es2015_shorthand_properties.as_mut().map(|t| t.transform_object_property(prop));
|
||||||
|
|
||||||
|
self.visit_property_key(&mut prop.key);
|
||||||
|
self.visit_expression(&mut prop.value);
|
||||||
|
if let Some(init) = &mut prop.init {
|
||||||
|
self.visit_expression(init);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ pub struct TransformOptions {
|
||||||
/// See <https://www.typescriptlang.org/tsconfig#target>
|
/// See <https://www.typescriptlang.org/tsconfig#target>
|
||||||
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
|
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
pub enum TransformTarget {
|
pub enum TransformTarget {
|
||||||
|
ES5,
|
||||||
ES2015,
|
ES2015,
|
||||||
ES2016,
|
ES2016,
|
||||||
ES2019,
|
ES2019,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
Passed: 86/1071
|
Passed: 91/1078
|
||||||
|
|
||||||
# babel-plugin-transform-class-properties
|
# babel-plugin-transform-class-properties
|
||||||
* Failed: assumption-constantSuper/complex-super-class/input.js
|
* Failed: assumption-constantSuper/complex-super-class/input.js
|
||||||
|
|
@ -750,6 +750,15 @@ Passed: 86/1071
|
||||||
* Failed: regression/4403/input.js
|
* Failed: regression/4403/input.js
|
||||||
* Passed: exponentiation-operator/binary/input.js
|
* Passed: exponentiation-operator/binary/input.js
|
||||||
|
|
||||||
|
# babel-plugin-transform-shorthand-properties
|
||||||
|
* Failed: shorthand-properties/method-type-annotations/input.js
|
||||||
|
* Failed: shorthand-properties/shorthand-comments/input.js
|
||||||
|
* Passed: shorthand-properties/method-plain/input.js
|
||||||
|
* Passed: shorthand-properties/proto/input.js
|
||||||
|
* Passed: shorthand-properties/shorthand-mixed/input.js
|
||||||
|
* Passed: shorthand-properties/shorthand-multiple/input.js
|
||||||
|
* Passed: shorthand-properties/shorthand-single/input.js
|
||||||
|
|
||||||
# babel-plugin-transform-typescript
|
# babel-plugin-transform-typescript
|
||||||
* Failed: class/abstract-class-decorated/input.ts
|
* Failed: class/abstract-class-decorated/input.ts
|
||||||
* Failed: class/abstract-class-decorated-method/input.ts
|
* Failed: class/abstract-class-decorated-method/input.ts
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,8 @@ pub fn babel(options: &BabelOptions) {
|
||||||
"babel-plugin-transform-async-to-generator",
|
"babel-plugin-transform-async-to-generator",
|
||||||
// ES2016
|
// ES2016
|
||||||
"babel-plugin-transform-exponentiation-operator",
|
"babel-plugin-transform-exponentiation-operator",
|
||||||
|
// ES2015
|
||||||
|
"babel-plugin-transform-shorthand-properties",
|
||||||
// TypeScript
|
// TypeScript
|
||||||
"babel-plugin-transform-typescript",
|
"babel-plugin-transform-typescript",
|
||||||
// React
|
// React
|
||||||
|
|
@ -133,7 +135,7 @@ fn babel_test(input_path: &Path, options: &BabelOptions) -> bool {
|
||||||
let expected = output_path.and_then(|path| fs::read_to_string(path).ok());
|
let expected = output_path.and_then(|path| fs::read_to_string(path).ok());
|
||||||
if let Some(expected) = &expected {
|
if let Some(expected) = &expected {
|
||||||
let transform_options = TransformOptions {
|
let transform_options = TransformOptions {
|
||||||
target: TransformTarget::ES2015,
|
target: TransformTarget::ES5,
|
||||||
react: Some(TransformReactOptions::default()),
|
react: Some(TransformReactOptions::default()),
|
||||||
};
|
};
|
||||||
let program = allocator.alloc(ret.program);
|
let program = allocator.alloc(ret.program);
|
||||||
|
|
@ -146,7 +148,7 @@ fn babel_test(input_path: &Path, options: &BabelOptions) -> bool {
|
||||||
let passed = trim_transformed == trim_expected;
|
let passed = trim_transformed == trim_expected;
|
||||||
if filtered {
|
if filtered {
|
||||||
println!("Expected:\n");
|
println!("Expected:\n");
|
||||||
println!("{expected:?}\n");
|
println!("{expected}\n");
|
||||||
println!("Transformed:\n");
|
println!("Transformed:\n");
|
||||||
println!("{transformed}\n");
|
println!("{transformed}\n");
|
||||||
println!("Diff:\n");
|
println!("Diff:\n");
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue