Skip to content

Commit

Permalink
fix(es/decorator): Add support for private access expressions in lega…
Browse files Browse the repository at this point in the history
…cy decorators (#9535)

**Related issue:**

- Closes #9429
  • Loading branch information
magic-akari authored Sep 6, 2024
1 parent c43dbad commit 62ed065
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 9 deletions.
6 changes: 6 additions & 0 deletions .changeset/nasty-ligers-reflect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
swc_ecma_transforms_proposal: patch
swc_core: patch
---

fix(es/decorator): Add support for private access expressions in legacy decorators
20 changes: 20 additions & 0 deletions crates/swc/tests/fixture/issues-9xxx/9429/input/.swcrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": true,
"decorators": true
},
"target": "es2022",
"loose": false,
"minify": {
"compress": false,
"mangle": false
}
},
"module": {
"type": "es6"
},
"minify": false,
"isModule": true
}
25 changes: 25 additions & 0 deletions crates/swc/tests/fixture/issues-9xxx/9429/input/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
class Foo {
static #loggedMethod<Args extends unknown[], Ret>(
_prototype: typeof Foo.prototype,
_propertyKey: string,
descriptor: TypedPropertyDescriptor<(this: Foo, ...args: Args) => Promise<Ret>>,
) {
const method = descriptor.value!;
descriptor.value = function (...args) {
try {
console.log("before");
return method.apply(this, args);
} finally {
console.log("after");
}
};
}

@(Foo.#loggedMethod)
public greet(): any {
console.log("hi");
}
}

const foo = new Foo();
foo.greet();
24 changes: 24 additions & 0 deletions crates/swc/tests/fixture/issues-9xxx/9429/input/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
class Foo {
static {
class Bar {
static #x() {}

@Bar.#x
foo() {}
}
}

static #y() {}

@Foo.#y
public foo() {}

static {
class Bar {
static #x() {}

@Bar.#x
foo() {}
}
}
}
24 changes: 24 additions & 0 deletions crates/swc/tests/fixture/issues-9xxx/9429/output/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
var _ts_decorate = require("@swc/helpers/_/_ts_decorate");
class Foo {
static #loggedMethod(_prototype, _propertyKey, descriptor) {
const method = descriptor.value;
descriptor.value = function(...args) {
try {
console.log("before");
return method.apply(this, args);
} finally{
console.log("after");
}
};
}
greet() {
console.log("hi");
}
static{
_ts_decorate._([
Foo.#loggedMethod
], Foo.prototype, "greet", null);
}
}
const foo = new Foo();
foo.greet();
32 changes: 32 additions & 0 deletions crates/swc/tests/fixture/issues-9xxx/9429/output/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
var _ts_decorate = require("@swc/helpers/_/_ts_decorate");
class Foo {
static{
class Bar {
static #x() {}
foo() {}
static{
_ts_decorate._([
Bar.#x
], Bar.prototype, "foo", null);
}
}
}
static #y() {}
foo() {}
static{
class Bar {
static #x() {}
foo() {}
static{
_ts_decorate._([
Bar.#x
], Bar.prototype, "foo", null);
}
}
}
static{
_ts_decorate._([
Foo.#y
], Foo.prototype, "foo", null);
}
}
69 changes: 60 additions & 9 deletions crates/swc_ecma_transforms_proposal/src/decorators/legacy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub(super) fn new(metadata: bool) -> TscDecorator {
enums: Default::default(),
vars: Default::default(),
appended_exprs: Default::default(),
appended_private_access_exprs: Default::default(),
prepended_exprs: Default::default(),
class_name: Default::default(),

Expand All @@ -41,6 +42,7 @@ pub(super) struct TscDecorator {
/// Used for computed keys, and this variables are not initialized.
vars: Vec<VarDeclarator>,
appended_exprs: Vec<Box<Expr>>,
appended_private_access_exprs: Vec<Box<Expr>>,
prepended_exprs: Vec<Box<Expr>>,

class_name: Option<Ident>,
Expand Down Expand Up @@ -155,6 +157,17 @@ impl TscDecorator {
prop_name_to_expr_value(k.clone())
}

fn has_private_access(mut expr: &Expr) -> bool {
while let Some(MemberExpr { obj, prop, .. }) = expr.as_member() {
if prop.is_private_name() {
return true;
}
expr = obj;
}

false
}

/// Creates `__decorate` calls.
fn add_decorate_call(
&mut self,
Expand All @@ -163,10 +176,17 @@ impl TscDecorator {
key: ExprOrSpread,
mut desc: ExprOrSpread,
) {
let mut has_private_access = false;
let decorators = ArrayLit {
span: DUMMY_SP,
elems: decorators
.into_iter()
.inspect(|e| {
if has_private_access {
return;
}
has_private_access = Self::has_private_access(e);
})
.map(|mut v| {
remove_span(&mut v);

Expand All @@ -180,15 +200,18 @@ impl TscDecorator {
remove_span(&mut target.expr);
remove_span(&mut desc.expr);

self.appended_exprs.push(
CallExpr {
span: DUMMY_SP,
callee: helper!(ts, ts_decorate),
args: vec![decorators, target, key, desc],
..Default::default()
}
.into(),
);
let expr = CallExpr {
callee: helper!(ts, ts_decorate),
args: vec![decorators, target, key, desc],
..Default::default()
}
.into();

if has_private_access {
self.appended_private_access_exprs.push(expr);
} else {
self.appended_exprs.push(expr);
}
}
}

Expand Down Expand Up @@ -235,6 +258,8 @@ impl Visit for TscDecorator {

impl VisitMut for TscDecorator {
fn visit_mut_class(&mut self, n: &mut Class) {
let appended_private = self.appended_private_access_exprs.take();

n.visit_mut_with(&mut ParamMetadata);

if self.metadata {
Expand All @@ -245,6 +270,32 @@ impl VisitMut for TscDecorator {

n.visit_mut_children_with(self);

let appended_private =
mem::replace(&mut self.appended_private_access_exprs, appended_private);

if !appended_private.is_empty() {
let expr = if appended_private.len() == 1 {
*appended_private.into_iter().next().unwrap()
} else {
SeqExpr {
exprs: appended_private,
..Default::default()
}
.into()
};

n.body.push(
StaticBlock {
body: BlockStmt {
stmts: vec![expr.into_stmt()],
..Default::default()
},
..Default::default()
}
.into(),
)
}

if let Some(class_name) = self.class_name.clone() {
if !n.decorators.is_empty() {
let decorators = ArrayLit {
Expand Down

0 comments on commit 62ed065

Please sign in to comment.