mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
feat(transformer): class properties transform skeleton (#7038)
Skeleton of class properties transform. #7011 contains WIP implementation.
This commit is contained in:
parent
934cb5e746
commit
1d906c64e7
5 changed files with 144 additions and 10 deletions
94
crates/oxc_transformer/src/es2022/class_properties.rs
Normal file
94
crates/oxc_transformer/src/es2022/class_properties.rs
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
//! ES2022: Class Properties
|
||||
//!
|
||||
//! This plugin transforms class properties to initializers inside class constructor.
|
||||
//!
|
||||
//! > This plugin is included in `preset-env`, in ES2022
|
||||
//!
|
||||
//! ## Example
|
||||
//!
|
||||
//! Input:
|
||||
//! ```js
|
||||
//! class C {
|
||||
//! foo = 123;
|
||||
//! #bar = 456;
|
||||
//! }
|
||||
//!
|
||||
//! let x = 123;
|
||||
//! class D extends S {
|
||||
//! foo = x;
|
||||
//! constructor(x) {
|
||||
//! if (x) {
|
||||
//! let s = super(x);
|
||||
//! } else {
|
||||
//! super(x);
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Output:
|
||||
//! ```js
|
||||
//! var _bar = /*#__PURE__*/ new WeakMap();
|
||||
//! class C {
|
||||
//! constructor() {
|
||||
//! babelHelpers.defineProperty(this, "foo", 123);
|
||||
//! babelHelpers.classPrivateFieldInitSpec(this, _bar, 456);
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! let x = 123;
|
||||
//! class D extends S {
|
||||
//! constructor(_x) {
|
||||
//! if (_x) {
|
||||
//! let s = (super(_x), babelHelpers.defineProperty(this, "foo", x));
|
||||
//! } else {
|
||||
//! super(_x);
|
||||
//! babelHelpers.defineProperty(this, "foo", x);
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Implementation
|
||||
//!
|
||||
//! WORK IN PROGRESS. INCOMPLETE.
|
||||
//!
|
||||
//! Implementation based on [@babel/plugin-transform-class-properties](https://babel.dev/docs/babel-plugin-transform-class-properties).
|
||||
//!
|
||||
//! ## References:
|
||||
//! * Babel plugin implementation:
|
||||
//! * <https://github.com/babel/babel/tree/main/packages/babel-plugin-transform-class-properties>
|
||||
//! * <https://github.com/babel/babel/blob/main/packages/babel-helper-create-class-features-plugin/src/index.ts>
|
||||
//! * <https://github.com/babel/babel/blob/main/packages/babel-helper-create-class-features-plugin/src/fields.ts>
|
||||
//! * Class properties TC39 proposal: <https://github.com/tc39/proposal-class-fields>
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_traverse::{Traverse, TraverseCtx};
|
||||
|
||||
use crate::TransformCtx;
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, Deserialize)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
pub struct ClassPropertiesOptions {
|
||||
#[serde(alias = "loose")]
|
||||
pub(crate) set_public_class_fields: bool,
|
||||
}
|
||||
|
||||
pub struct ClassProperties<'a, 'ctx> {
|
||||
#[expect(dead_code)]
|
||||
options: ClassPropertiesOptions,
|
||||
#[expect(dead_code)]
|
||||
ctx: &'ctx TransformCtx<'a>,
|
||||
}
|
||||
|
||||
impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
|
||||
pub fn new(options: ClassPropertiesOptions, ctx: &'ctx TransformCtx<'a>) -> Self {
|
||||
Self { options, ctx }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'ctx> Traverse<'a> for ClassProperties<'a, 'ctx> {
|
||||
fn enter_class_body(&mut self, _body: &mut ClassBody<'a>, _ctx: &mut TraverseCtx<'a>) {}
|
||||
}
|
||||
|
|
@ -1,29 +1,44 @@
|
|||
use oxc_ast::ast::*;
|
||||
use oxc_traverse::{Traverse, TraverseCtx};
|
||||
|
||||
use crate::TransformCtx;
|
||||
|
||||
mod class_properties;
|
||||
mod class_static_block;
|
||||
mod options;
|
||||
|
||||
use class_properties::ClassProperties;
|
||||
pub use class_properties::ClassPropertiesOptions;
|
||||
use class_static_block::ClassStaticBlock;
|
||||
|
||||
pub use options::ES2022Options;
|
||||
|
||||
pub struct ES2022 {
|
||||
pub struct ES2022<'a, 'ctx> {
|
||||
options: ES2022Options,
|
||||
// Plugins
|
||||
class_static_block: ClassStaticBlock,
|
||||
class_properties: Option<ClassProperties<'a, 'ctx>>,
|
||||
}
|
||||
|
||||
impl ES2022 {
|
||||
pub fn new(options: ES2022Options) -> Self {
|
||||
Self { options, class_static_block: ClassStaticBlock::new() }
|
||||
impl<'a, 'ctx> ES2022<'a, 'ctx> {
|
||||
pub fn new(options: ES2022Options, ctx: &'ctx TransformCtx<'a>) -> Self {
|
||||
Self {
|
||||
options,
|
||||
class_static_block: ClassStaticBlock::new(),
|
||||
class_properties: options
|
||||
.class_properties
|
||||
.map(|options| ClassProperties::new(options, ctx)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Traverse<'a> for ES2022 {
|
||||
impl<'a, 'ctx> Traverse<'a> for ES2022<'a, 'ctx> {
|
||||
fn enter_class_body(&mut self, body: &mut ClassBody<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if self.options.class_static_block {
|
||||
self.class_static_block.enter_class_body(body, ctx);
|
||||
}
|
||||
if let Some(class_properties) = &mut self.class_properties {
|
||||
class_properties.enter_class_body(body, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Default, Clone, Deserialize)]
|
||||
use super::ClassPropertiesOptions;
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, Deserialize)]
|
||||
#[serde(default, rename_all = "camelCase", deny_unknown_fields)]
|
||||
pub struct ES2022Options {
|
||||
#[serde(skip)]
|
||||
pub class_static_block: bool,
|
||||
pub class_properties: Option<ClassPropertiesOptions>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ impl<'a> Transformer<'a> {
|
|||
.is_typescript()
|
||||
.then(|| TypeScript::new(&self.options.typescript, &self.ctx)),
|
||||
x1_jsx: Jsx::new(self.options.jsx, ast_builder, &self.ctx),
|
||||
x2_es2022: ES2022::new(self.options.es2022),
|
||||
x2_es2022: ES2022::new(self.options.es2022, &self.ctx),
|
||||
x2_es2021: ES2021::new(self.options.es2021, &self.ctx),
|
||||
x2_es2020: ES2020::new(self.options.es2020, &self.ctx),
|
||||
x2_es2019: ES2019::new(self.options.es2019),
|
||||
|
|
@ -117,7 +117,7 @@ struct TransformerImpl<'a, 'ctx> {
|
|||
// NOTE: all callbacks must run in order.
|
||||
x0_typescript: Option<TypeScript<'a, 'ctx>>,
|
||||
x1_jsx: Jsx<'a, 'ctx>,
|
||||
x2_es2022: ES2022,
|
||||
x2_es2022: ES2022<'a, 'ctx>,
|
||||
x2_es2021: ES2021<'a, 'ctx>,
|
||||
x2_es2020: ES2020<'a, 'ctx>,
|
||||
x2_es2019: ES2019,
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use crate::{
|
|||
es2019::ES2019Options,
|
||||
es2020::ES2020Options,
|
||||
es2021::ES2021Options,
|
||||
es2022::ES2022Options,
|
||||
es2022::{ClassPropertiesOptions, ES2022Options},
|
||||
jsx::JsxOptions,
|
||||
regexp::RegExpOptions,
|
||||
typescript::TypeScriptOptions,
|
||||
|
|
@ -109,7 +109,10 @@ impl TransformOptions {
|
|||
es2019: ES2019Options { optional_catch_binding: true },
|
||||
es2020: ES2020Options { nullish_coalescing_operator: true },
|
||||
es2021: ES2021Options { logical_assignment_operators: true },
|
||||
es2022: ES2022Options { class_static_block: true },
|
||||
es2022: ES2022Options {
|
||||
class_static_block: true,
|
||||
class_properties: Some(ClassPropertiesOptions::default()),
|
||||
},
|
||||
helper_loader: HelperLoaderOptions {
|
||||
mode: HelperLoaderMode::Runtime,
|
||||
..Default::default()
|
||||
|
|
@ -165,6 +168,9 @@ impl TryFrom<&EnvOptions> for TransformOptions {
|
|||
},
|
||||
es2022: ES2022Options {
|
||||
class_static_block: o.can_enable_plugin("transform-class-static-block"),
|
||||
class_properties: o
|
||||
.can_enable_plugin("transform-class-properties")
|
||||
.then(Default::default),
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
|
|
@ -352,6 +358,22 @@ impl TryFrom<&BabelOptions> for TransformOptions {
|
|||
let plugin_name = "transform-class-static-block";
|
||||
options.get_plugin(plugin_name).is_some() || env.es2022.class_static_block
|
||||
},
|
||||
class_properties: {
|
||||
let plugin_name = "transform-class-properties";
|
||||
options
|
||||
.get_plugin(plugin_name)
|
||||
.map(|o| {
|
||||
o.and_then(|options| {
|
||||
serde_json::from_value::<ClassPropertiesOptions>(options)
|
||||
.inspect_err(|err| {
|
||||
report_error(plugin_name, err, false, &mut errors);
|
||||
})
|
||||
.ok()
|
||||
})
|
||||
.unwrap_or_default()
|
||||
})
|
||||
.or(env.es2022.class_properties)
|
||||
},
|
||||
};
|
||||
|
||||
if !errors.is_empty() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue