Skip to content

Commit 62ed065

Browse files
authoredSep 6, 2024··
fix(es/decorator): Add support for private access expressions in legacy decorators (#9535)
**Related issue:** - Closes #9429
1 parent c43dbad commit 62ed065

File tree

7 files changed

+191
-9
lines changed

7 files changed

+191
-9
lines changed
 

‎.changeset/nasty-ligers-reflect.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
swc_ecma_transforms_proposal: patch
3+
swc_core: patch
4+
---
5+
6+
fix(es/decorator): Add support for private access expressions in legacy decorators
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"jsc": {
3+
"parser": {
4+
"syntax": "typescript",
5+
"tsx": true,
6+
"decorators": true
7+
},
8+
"target": "es2022",
9+
"loose": false,
10+
"minify": {
11+
"compress": false,
12+
"mangle": false
13+
}
14+
},
15+
"module": {
16+
"type": "es6"
17+
},
18+
"minify": false,
19+
"isModule": true
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
class Foo {
2+
static #loggedMethod<Args extends unknown[], Ret>(
3+
_prototype: typeof Foo.prototype,
4+
_propertyKey: string,
5+
descriptor: TypedPropertyDescriptor<(this: Foo, ...args: Args) => Promise<Ret>>,
6+
) {
7+
const method = descriptor.value!;
8+
descriptor.value = function (...args) {
9+
try {
10+
console.log("before");
11+
return method.apply(this, args);
12+
} finally {
13+
console.log("after");
14+
}
15+
};
16+
}
17+
18+
@(Foo.#loggedMethod)
19+
public greet(): any {
20+
console.log("hi");
21+
}
22+
}
23+
24+
const foo = new Foo();
25+
foo.greet();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
class Foo {
2+
static {
3+
class Bar {
4+
static #x() {}
5+
6+
@Bar.#x
7+
foo() {}
8+
}
9+
}
10+
11+
static #y() {}
12+
13+
@Foo.#y
14+
public foo() {}
15+
16+
static {
17+
class Bar {
18+
static #x() {}
19+
20+
@Bar.#x
21+
foo() {}
22+
}
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
var _ts_decorate = require("@swc/helpers/_/_ts_decorate");
2+
class Foo {
3+
static #loggedMethod(_prototype, _propertyKey, descriptor) {
4+
const method = descriptor.value;
5+
descriptor.value = function(...args) {
6+
try {
7+
console.log("before");
8+
return method.apply(this, args);
9+
} finally{
10+
console.log("after");
11+
}
12+
};
13+
}
14+
greet() {
15+
console.log("hi");
16+
}
17+
static{
18+
_ts_decorate._([
19+
Foo.#loggedMethod
20+
], Foo.prototype, "greet", null);
21+
}
22+
}
23+
const foo = new Foo();
24+
foo.greet();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
var _ts_decorate = require("@swc/helpers/_/_ts_decorate");
2+
class Foo {
3+
static{
4+
class Bar {
5+
static #x() {}
6+
foo() {}
7+
static{
8+
_ts_decorate._([
9+
Bar.#x
10+
], Bar.prototype, "foo", null);
11+
}
12+
}
13+
}
14+
static #y() {}
15+
foo() {}
16+
static{
17+
class Bar {
18+
static #x() {}
19+
foo() {}
20+
static{
21+
_ts_decorate._([
22+
Bar.#x
23+
], Bar.prototype, "foo", null);
24+
}
25+
}
26+
}
27+
static{
28+
_ts_decorate._([
29+
Foo.#y
30+
], Foo.prototype, "foo", null);
31+
}
32+
}

‎crates/swc_ecma_transforms_proposal/src/decorators/legacy/mod.rs

+60-9
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pub(super) fn new(metadata: bool) -> TscDecorator {
2626
enums: Default::default(),
2727
vars: Default::default(),
2828
appended_exprs: Default::default(),
29+
appended_private_access_exprs: Default::default(),
2930
prepended_exprs: Default::default(),
3031
class_name: Default::default(),
3132

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

4648
class_name: Option<Ident>,
@@ -155,6 +157,17 @@ impl TscDecorator {
155157
prop_name_to_expr_value(k.clone())
156158
}
157159

160+
fn has_private_access(mut expr: &Expr) -> bool {
161+
while let Some(MemberExpr { obj, prop, .. }) = expr.as_member() {
162+
if prop.is_private_name() {
163+
return true;
164+
}
165+
expr = obj;
166+
}
167+
168+
false
169+
}
170+
158171
/// Creates `__decorate` calls.
159172
fn add_decorate_call(
160173
&mut self,
@@ -163,10 +176,17 @@ impl TscDecorator {
163176
key: ExprOrSpread,
164177
mut desc: ExprOrSpread,
165178
) {
179+
let mut has_private_access = false;
166180
let decorators = ArrayLit {
167181
span: DUMMY_SP,
168182
elems: decorators
169183
.into_iter()
184+
.inspect(|e| {
185+
if has_private_access {
186+
return;
187+
}
188+
has_private_access = Self::has_private_access(e);
189+
})
170190
.map(|mut v| {
171191
remove_span(&mut v);
172192

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

183-
self.appended_exprs.push(
184-
CallExpr {
185-
span: DUMMY_SP,
186-
callee: helper!(ts, ts_decorate),
187-
args: vec![decorators, target, key, desc],
188-
..Default::default()
189-
}
190-
.into(),
191-
);
203+
let expr = CallExpr {
204+
callee: helper!(ts, ts_decorate),
205+
args: vec![decorators, target, key, desc],
206+
..Default::default()
207+
}
208+
.into();
209+
210+
if has_private_access {
211+
self.appended_private_access_exprs.push(expr);
212+
} else {
213+
self.appended_exprs.push(expr);
214+
}
192215
}
193216
}
194217

@@ -235,6 +258,8 @@ impl Visit for TscDecorator {
235258

236259
impl VisitMut for TscDecorator {
237260
fn visit_mut_class(&mut self, n: &mut Class) {
261+
let appended_private = self.appended_private_access_exprs.take();
262+
238263
n.visit_mut_with(&mut ParamMetadata);
239264

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

246271
n.visit_mut_children_with(self);
247272

273+
let appended_private =
274+
mem::replace(&mut self.appended_private_access_exprs, appended_private);
275+
276+
if !appended_private.is_empty() {
277+
let expr = if appended_private.len() == 1 {
278+
*appended_private.into_iter().next().unwrap()
279+
} else {
280+
SeqExpr {
281+
exprs: appended_private,
282+
..Default::default()
283+
}
284+
.into()
285+
};
286+
287+
n.body.push(
288+
StaticBlock {
289+
body: BlockStmt {
290+
stmts: vec![expr.into_stmt()],
291+
..Default::default()
292+
},
293+
..Default::default()
294+
}
295+
.into(),
296+
)
297+
}
298+
248299
if let Some(class_name) = self.class_name.clone() {
249300
if !n.decorators.is_empty() {
250301
let decorators = ArrayLit {

0 commit comments

Comments
 (0)
Please sign in to comment.