Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions crates/oxc_transformer/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ pub struct TransformCtx<'a> {
pub statement_injector: StatementInjectorStore<'a>,
/// Manage inserting statements at top of program globally
pub top_level_statements: TopLevelStatementsStore<'a>,

// State for multiple plugins interacting
/// `true` if class properties plugin is enabled
pub is_class_properties_plugin_enabled: bool,
}

impl TransformCtx<'_> {
Expand All @@ -65,6 +69,7 @@ impl TransformCtx<'_> {
var_declarations: VarDeclarationsStore::new(),
statement_injector: StatementInjectorStore::new(),
top_level_statements: TopLevelStatementsStore::new(),
is_class_properties_plugin_enabled: options.env.es2022.class_properties.is_some(),
}
}

Expand Down
42 changes: 40 additions & 2 deletions crates/oxc_transformer/src/decorator/legacy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@ use oxc_span::SPAN;
use oxc_syntax::operator::AssignmentOperator;
use oxc_traverse::{Ancestor, BoundIdentifier, Traverse, TraverseCtx};

use crate::{Helper, TransformCtx, utils::ast_builder::create_prototype_member};
use crate::{
Helper, TransformCtx,
utils::ast_builder::{create_assignment, create_prototype_member},
};
use metadata::LegacyDecoratorMetadata;

#[derive(Default)]
Expand Down Expand Up @@ -466,17 +469,52 @@ impl<'a> LegacyDecorator<'a, '_> {
let mut decoration_stmts =
self.transform_decorators_of_class_elements(class, &class_binding, ctx);

let class_alias_with_this_assignment = if self.ctx.is_class_properties_plugin_enabled {
None
} else {
// If we're emitting to ES2022 or later then we need to reassign the class alias before
// static initializers are evaluated.
// <https://github.com/microsoft/TypeScript/blob/b86ab7dbe0eb2f1c9a624486d72590d638495c97/src/compiler/transformers/legacyDecorators.ts#L345-L366>
class_alias_binding.as_ref().and_then(|class_alias_binding| {
let has_static_field_or_block = class.body.body.iter().any(|element| {
matches!(element, ClassElement::StaticBlock(_))
|| matches!(element, ClassElement::PropertyDefinition(prop)
if prop.r#static
)
});

if has_static_field_or_block {
// `_Class = this`;
let class_alias_with_this_assignment = ctx.ast.statement_expression(
SPAN,
create_assignment(class_alias_binding, ctx.ast.expression_this(SPAN), ctx),
);
let body = ctx.ast.vec1(class_alias_with_this_assignment);
let scope_id = ctx.create_child_scope_of_current(ScopeFlags::ClassStaticBlock);
let element =
ctx.ast.class_element_static_block_with_scope_id(SPAN, body, scope_id);
Some(element)
} else {
None
}
})
};

Comment thread
Dunqing marked this conversation as resolved.
if has_private_in_expression_in_decorator {
let stmts = mem::replace(&mut decoration_stmts, ctx.ast.vec());
Self::insert_decorations_into_class_static_block(class, stmts, ctx);
} else {
decoration_stmts.push(constructor_decoration);
self.class_decorated_data = Some(ClassDecoratedData {
binding: class_binding,
alias_binding: class_alias_binding,
alias_binding: class_alias_binding.clone(),
decorations: decoration_stmts,
});
}

if let Some(class_alias_with_this_assignment) = class_alias_with_this_assignment {
class.body.body.insert(0, class_alias_with_this_assignment);
}
}

/// Transform class to a [`VariableDeclarator`], whose binding name is the same as class.
Expand Down
11 changes: 4 additions & 7 deletions crates/oxc_transformer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,10 @@ impl<'a> Transformer<'a> {
.proposals
.explicit_resource_management
.then(|| ExplicitResourceManagement::new(&self.ctx)),
x0_typescript: program.source_type.is_typescript().then(|| {
TypeScript::new(
&self.typescript,
self.env.es2022.class_properties.is_some(),
&self.ctx,
)
}),
x0_typescript: program
.source_type
.is_typescript()
.then(|| TypeScript::new(&self.typescript, &self.ctx)),
x1_jsx: Jsx::new(self.jsx, self.env.es2018.object_rest_spread, ast_builder, &self.ctx),
x2_es2022: ES2022::new(
self.env.es2022,
Expand Down
18 changes: 7 additions & 11 deletions crates/oxc_transformer/src/typescript/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,10 @@ pub struct TypeScript<'a, 'ctx> {
rewrite_extensions: Option<TypeScriptRewriteExtensions>,
// Options
remove_class_fields_without_initializer: bool,
// State
/// Whether class properties plugin is enabled
is_class_properties_enabled: bool,
}

impl<'a, 'ctx> TypeScript<'a, 'ctx> {
pub fn new(
options: &TypeScriptOptions,
is_class_properties_enabled: bool,
ctx: &'ctx TransformCtx<'a>,
) -> Self {
pub fn new(options: &TypeScriptOptions, ctx: &'ctx TransformCtx<'a>) -> Self {
Self {
ctx,
annotations: TypeScriptAnnotations::new(options, ctx),
Expand All @@ -71,7 +64,6 @@ impl<'a, 'ctx> TypeScript<'a, 'ctx> {
rewrite_extensions: TypeScriptRewriteExtensions::new(options),
remove_class_fields_without_initializer: !options.allow_declare_fields
|| options.remove_class_fields_without_initializer,
is_class_properties_enabled,
}
}
}
Expand Down Expand Up @@ -128,7 +120,9 @@ impl<'a> Traverse<'a> for TypeScript<'a, '_> {

// Avoid converting class fields when class-properties plugin is enabled, that plugin has covered all
// this transformation does.
if !self.is_class_properties_enabled && self.ctx.assumptions.set_public_class_fields {
if !self.ctx.is_class_properties_plugin_enabled
&& self.ctx.assumptions.set_public_class_fields
{
self.transform_class_fields(class, ctx);
}
}
Expand Down Expand Up @@ -187,7 +181,9 @@ impl<'a> Traverse<'a> for TypeScript<'a, '_> {
ctx: &mut TraverseCtx<'a>,
) {
self.annotations.enter_method_definition(def, ctx);
if self.is_class_properties_enabled || !self.ctx.assumptions.set_public_class_fields {
if self.ctx.is_class_properties_plugin_enabled
|| !self.ctx.assumptions.set_public_class_fields
{
Self::transform_class_constructor(def, ctx);
}
}
Expand Down
44 changes: 42 additions & 2 deletions tasks/transform_conformance/snapshots/oxc.snap.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
commit: 578ac4df

Passed: 153/255
Passed: 153/257

# All Passed:
* babel-plugin-transform-class-static-block
Expand Down Expand Up @@ -497,7 +497,7 @@ after transform: SymbolId(4): ScopeId(1)
rebuilt : SymbolId(5): ScopeId(4)


# legacy-decorators (4/74)
# legacy-decorators (4/76)
* oxc/metadata/abstract-class/input.ts
Symbol reference IDs mismatch for "Dependency":
after transform: SymbolId(1): [ReferenceId(1), ReferenceId(2), ReferenceId(3)]
Expand Down Expand Up @@ -582,6 +582,46 @@ Symbol span mismatch for "C":
after transform: SymbolId(3): Span { start: 0, end: 0 }
rebuilt : SymbolId(3): Span { start: 106, end: 107 }

* oxc/static-field/input.ts
Scope children mismatch:
after transform: ScopeId(0): [ScopeId(1), ScopeId(3), ScopeId(4)]
rebuilt : ScopeId(0): [ScopeId(1), ScopeId(3)]
Scope children mismatch:
after transform: ScopeId(3): []
rebuilt : ScopeId(3): [ScopeId(4)]
Scope flags mismatch:
after transform: ScopeId(4): ScopeFlags(ClassStaticBlock)
rebuilt : ScopeId(4): ScopeFlags(StrictMode | ClassStaticBlock)
Scope parent mismatch:
after transform: ScopeId(4): Some(ScopeId(0))
rebuilt : ScopeId(4): Some(ScopeId(3))
Symbol span mismatch for "Foo":
after transform: SymbolId(2): Span { start: 103, end: 106 }
rebuilt : SymbolId(3): Span { start: 0, end: 0 }
Symbol reference IDs mismatch for "Foo":
after transform: SymbolId(2): [ReferenceId(4), ReferenceId(6), ReferenceId(8)]
rebuilt : SymbolId(3): [ReferenceId(5), ReferenceId(9)]
Symbol span mismatch for "Foo":
after transform: SymbolId(3): Span { start: 0, end: 0 }
rebuilt : SymbolId(4): Span { start: 103, end: 106 }
Unresolved references mismatch:
after transform: ["ClassDecorator", "babelHelpers", "console"]
rebuilt : ["babelHelpers", "console"]

* oxc/static-field-with-class-properties/input.ts
Symbol span mismatch for "Foo":
after transform: SymbolId(2): Span { start: 103, end: 106 }
rebuilt : SymbolId(3): Span { start: 0, end: 0 }
Symbol reference IDs mismatch for "Foo":
after transform: SymbolId(2): [ReferenceId(4), ReferenceId(6), ReferenceId(8), ReferenceId(10)]
rebuilt : SymbolId(3): [ReferenceId(4), ReferenceId(6), ReferenceId(10)]
Symbol span mismatch for "Foo":
after transform: SymbolId(3): Span { start: 0, end: 0 }
rebuilt : SymbolId(4): Span { start: 103, end: 106 }
Unresolved references mismatch:
after transform: ["ClassDecorator", "babelHelpers", "console"]
rebuilt : ["babelHelpers", "console"]

* oxc/with-class-private-properties/input.ts
Symbol span mismatch for "C":
after transform: SymbolId(0): Span { start: 11, end: 12 }
Expand Down
2 changes: 1 addition & 1 deletion tasks/transform_conformance/snapshots/oxc_exec.snap.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ commit: 578ac4df

node: v22.14.0

Passed: 6 of 8 (75.00%)
Passed: 8 of 10 (80.00%)

Failures:

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
function Bar(): ClassDecorator {
return (_target) => {
console.log(Bar.name)
}
}

@Bar()
class Foo {
static foo = `${Foo.name}`;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
function Bar(): ClassDecorator {
return (_target) => {
console.log(Bar.name)
}
}

@Bar()
class Foo {
static foo = `${Foo.name}`;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"plugins": [
"transform-legacy-decorator",
"transform-class-properties"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
var _Foo;
function Bar() {
return (_target) => {
console.log(Bar.name);
};
}
let Foo = _Foo = class Foo {};
babelHelpers.defineProperty(Foo, "foo", `${_Foo.name}`);
Foo = _Foo = babelHelpers.decorate([Bar()], Foo);
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
function Bar(): ClassDecorator {
return (_target) => {
console.log(Bar.name)
}
}

@Bar()
class Foo {
static foo = `${Foo.name}`;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
function Bar(): ClassDecorator {
return (_target) => {
console.log(Bar.name)
}
}

@Bar()
class Foo {
static foo = `${Foo.name}`;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"plugins": [
"transform-legacy-decorator"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
var _Foo;
function Bar() {
return (_target) => {
console.log(Bar.name);
};
}
let Foo = _Foo = class Foo {
static {
_Foo = this;
}
static foo = `${_Foo.name}`;
};
Foo = _Foo = babelHelpers.decorate([Bar()], Foo);
Loading