mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
fix(transformer): legacy decorator on computed property key leaves variable unassigned (#20430)
This PR has two fixes. The second fix exposed a pre-existing bug that needed to be fixed first. - 24a72d7bc: `create_read_write_target` was used for a simple `=` assignment when creating a temp var for decorated computed property keys. Should be `create_write_target`. - f0affc6f4: `transform_class_on_exit` was removing all no-initializer properties unconditionally. But the legacy decorator embeds an assignment in the computed key (`[FIELD_NAME]` → `[_a = FIELD_NAME]`), so removing the property also removes the assignment, leaving `_a` undefined in `_decorate()`. Keep properties whose computed keys have side effects, matching TypeScript's behavior. Fixes #20418 --------- Co-authored-by: Dunqing <dengqing0821@gmail.com>
This commit is contained in:
1 parent
7c233f42d2
commit
11f96959b0
9 files changed
+48
-17
No files matched your search
@@ -1115,7 +1115,7 @@ impl<'a> LegacyDecorator<'a> {
|
||||
// Create a unique binding for the computed property key, and insert it outside of the class
|
||||
let binding = VarDeclarationsStore::create_uid_var_based_on_node(key, ctx);
|
||||
let operator = AssignmentOperator::Assign;
|
||||
let left = binding.create_read_write_target(ctx);
|
||||
let left = binding.create_write_target(ctx);
|
||||
let right = key.to_expression_mut().take_in(ctx.ast);
|
||||
let key_expr = ctx.ast.expression_assignment(SPAN, operator, left, right);
|
||||
*key = PropertyKey::from(key_expr);
|
||||
|
||||
@@ -206,11 +206,7 @@ impl<'a> TypeScript<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn transform_class_on_exit(
|
||||
&self,
|
||||
class: &mut Class<'a>,
|
||||
_ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
pub(super) fn transform_class_on_exit(&self, class: &mut Class<'a>, ctx: &TraverseCtx<'a>) {
|
||||
if !self.remove_class_fields_without_initializer {
|
||||
return;
|
||||
}
|
||||
@@ -220,6 +216,9 @@ impl<'a> TypeScript<'a> {
|
||||
&& prop.value.is_none()
|
||||
&& !prop.key.is_private_identifier()
|
||||
{
|
||||
if let Some(key) = prop.key.as_expression() {
|
||||
return key_needs_temp_var(key, ctx);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
true
|
||||
|
||||
@@ -22822,9 +22822,6 @@ semantic Error: tasks/coverage/typescript/tests/cases/conformance/esDecorators/c
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(0): ["C", "_decorate", "_decorateMetadata", "_field", "dec", "field3"]
|
||||
rebuilt : ScopeId(0): ["C", "_decorate", "_decorateMetadata", "_field", "field3"]
|
||||
Reference flags mismatch for "_field":
|
||||
after transform: ReferenceId(15): ReferenceFlags(Read | Write)
|
||||
rebuilt : ReferenceId(2): ReferenceFlags(Write)
|
||||
Reference symbol mismatch for "dec":
|
||||
after transform: SymbolId(0) "dec"
|
||||
rebuilt : <None>
|
||||
@@ -22864,9 +22861,6 @@ semantic Error: tasks/coverage/typescript/tests/cases/conformance/esDecorators/c
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(0): ["C", "_decorate", "_decorateMetadata", "_defineProperty", "_field", "_field2", "dec", "field3"]
|
||||
rebuilt : ScopeId(0): ["C", "_decorate", "_decorateMetadata", "_defineProperty", "_field", "_field2", "field3"]
|
||||
Reference flags mismatch for "_field":
|
||||
after transform: ReferenceId(15): ReferenceFlags(Read | Write)
|
||||
rebuilt : ReferenceId(4): ReferenceFlags(Write)
|
||||
Reference symbol mismatch for "dec":
|
||||
after transform: SymbolId(0) "dec"
|
||||
rebuilt : <None>
|
||||
@@ -22896,9 +22890,6 @@ rebuilt : SymbolId(7): Span { start: 199, end: 200 }
|
||||
Symbol reference IDs mismatch for "D":
|
||||
after transform: SymbolId(7): [ReferenceId(24)]
|
||||
rebuilt : SymbolId(7): []
|
||||
Reference flags mismatch for "_field":
|
||||
after transform: ReferenceId(16): ReferenceFlags(Read | Write)
|
||||
rebuilt : ReferenceId(2): ReferenceFlags(Write)
|
||||
Reference symbol mismatch for "dec":
|
||||
after transform: SymbolId(0) "dec"
|
||||
rebuilt : <None>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
commit: 87a048db
|
||||
|
||||
Passed: 204/337
|
||||
Passed: 206/339
|
||||
|
||||
# All Passed:
|
||||
* babel-plugin-transform-class-static-block
|
||||
@@ -555,7 +555,7 @@ x Output mismatch
|
||||
x Output mismatch
|
||||
|
||||
|
||||
# legacy-decorators (6/88)
|
||||
# legacy-decorators (8/90)
|
||||
* oxc/class-without-name-with-decorated_class/input.ts
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(0): ["dec"]
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
const FIELD_NAME = "myField";
|
||||
|
||||
function dec(target: any, key: string) {}
|
||||
|
||||
class MyModel {
|
||||
@dec
|
||||
[FIELD_NAME] = "value";
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
var _FIELD_NAME;
|
||||
const FIELD_NAME = "myField";
|
||||
function dec(target, key) {}
|
||||
class MyModel {
|
||||
[_FIELD_NAME = FIELD_NAME] = "value";
|
||||
}
|
||||
babelHelpers.decorate([dec], MyModel.prototype, _FIELD_NAME, void 0);
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
const FIELD_NAME = "myField";
|
||||
|
||||
function dec(target: any, key: string) {}
|
||||
|
||||
class MyModel {
|
||||
@dec
|
||||
[FIELD_NAME]: string;
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"plugins": [
|
||||
"transform-legacy-decorator",
|
||||
[
|
||||
"transform-typescript",
|
||||
{
|
||||
"removeClassFieldsWithoutInitializer": true
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
var _FIELD_NAME;
|
||||
const FIELD_NAME = "myField";
|
||||
function dec(target, key) {}
|
||||
class MyModel {
|
||||
[_FIELD_NAME = FIELD_NAME];
|
||||
}
|
||||
babelHelpers.decorate([dec], MyModel.prototype, _FIELD_NAME, void 0);
|
||||
Reference in new issue
Block a user