diff --git a/crates/swc_ecma_transforms_proposal/src/decorator_2022_03.rs b/crates/swc_ecma_transforms_proposal/src/decorator_2022_03.rs index 6bb1b266bad..1218f166106 100644 --- a/crates/swc_ecma_transforms_proposal/src/decorator_2022_03.rs +++ b/crates/swc_ecma_transforms_proposal/src/decorator_2022_03.rs @@ -276,39 +276,49 @@ impl Decorator202203 { } fn ensure_constructor<'a>(&mut self, c: &'a mut Class) -> &'a mut Constructor { - for member in c.body.iter_mut() { + let mut insert_index = 0; + for (i, member) in c.body.iter_mut().enumerate() { if let ClassMember::Constructor(constructor) = member { - return unsafe { - // Safety: We need polonius - transmute::<&mut Constructor, &'a mut Constructor>(constructor) - }; - } - } - - c.body - .insert(0, default_constructor(c.super_class.is_some()).into()); - - for member in c.body.iter_mut() { - if let ClassMember::Constructor(constructor) = member { - return constructor; - } - } - - unreachable!() - } - - fn ensure_identity_constructor<'a>(&mut self, c: &'a mut Class) -> &'a mut Constructor { - for member in c.body.iter_mut() { - if let ClassMember::Constructor(constructor) = member { - return unsafe { - // Safety: We need polonius - transmute::<&mut Constructor, &'a mut Constructor>(constructor) - }; + insert_index = i + 1; + // decorators occur before typescript's type strip, so skip ctor overloads + if constructor.body.is_some() { + return unsafe { + // Safety: We need polonius + transmute::<&mut Constructor, &'a mut Constructor>(constructor) + }; + } } } c.body.insert( - 0, + insert_index, + default_constructor(c.super_class.is_some()).into(), + ); + + if let Some(ClassMember::Constructor(c)) = c.body.get_mut(insert_index) { + c + } else { + unreachable!() + } + } + + fn ensure_identity_constructor<'a>(&mut self, c: &'a mut Class) -> &'a mut Constructor { + let mut insert_index = 0; + for (i, member) in c.body.iter_mut().enumerate() { + if let ClassMember::Constructor(constructor) = member { + insert_index = i + 1; + // decorators occur before typescript's type strip, so skip ctor overloads + if constructor.body.is_some() { + return unsafe { + // Safety: We need polonius + transmute::<&mut Constructor, &'a mut Constructor>(constructor) + }; + } + } + } + + c.body.insert( + insert_index, ClassMember::Constructor(Constructor { span: DUMMY_SP, key: PropName::Ident(quote_ident!("constructor")), @@ -322,13 +332,11 @@ impl Decorator202203 { }), ); - for member in c.body.iter_mut() { - if let ClassMember::Constructor(constructor) = member { - return constructor; - } + if let Some(ClassMember::Constructor(c)) = c.body.get_mut(insert_index) { + c + } else { + unreachable!() } - - unreachable!() } fn handle_super_class(&mut self, class: &mut Class) { diff --git a/crates/swc_ecma_transforms_proposal/tests/decorators.rs b/crates/swc_ecma_transforms_proposal/tests/decorators.rs index 9b070918c74..2fd93980d6f 100644 --- a/crates/swc_ecma_transforms_proposal/tests/decorators.rs +++ b/crates/swc_ecma_transforms_proposal/tests/decorators.rs @@ -23,6 +23,13 @@ fn syntax_default() -> Syntax { }) } +fn syntax_default_ts() -> Syntax { + Syntax::Typescript(TsConfig { + decorators: true, + ..Default::default() + }) +} + #[testing::fixture("tests/decorators/**/exec.js")] fn exec(input: PathBuf) { exec_inner(input) @@ -44,6 +51,7 @@ fn exec_inner(input: PathBuf) { #[testing::fixture("tests/decorators/**/input.js")] #[testing::fixture("tests/decorators/**/input.mjs")] +#[testing::fixture("tests/decorators/**/input.ts")] fn fixture(input: PathBuf) { fixture_inner(input) } @@ -57,7 +65,11 @@ fn fixture_inner(input: PathBuf) { )); test_fixture( - syntax_default(), + if input.to_string_lossy().ends_with(".ts") { + syntax_default_ts() + } else { + syntax_default() + }, &|t| create_pass(t.comments.clone(), &input), &input, &output, diff --git a/crates/swc_ecma_transforms_proposal/tests/decorators/issue-8631/1/input.ts b/crates/swc_ecma_transforms_proposal/tests/decorators/issue-8631/1/input.ts new file mode 100644 index 00000000000..d00f8c25e4e --- /dev/null +++ b/crates/swc_ecma_transforms_proposal/tests/decorators/issue-8631/1/input.ts @@ -0,0 +1,13 @@ +const decorate = (target: unknown, context: DecoratorContext) => { + console.log("decorated"); +}; + +export class Color { + constructor(hex: string); + constructor(r: number, g: number, b: number, a?: number); + constructor(r: string | number, g?: number, b?: number, a = 1) {} + + @decorate get rgba() { + return [0, 0, 0, 1]; + } +} diff --git a/crates/swc_ecma_transforms_proposal/tests/decorators/issue-8631/1/output.js b/crates/swc_ecma_transforms_proposal/tests/decorators/issue-8631/1/output.js new file mode 100644 index 00000000000..cf8cb217753 --- /dev/null +++ b/crates/swc_ecma_transforms_proposal/tests/decorators/issue-8631/1/output.js @@ -0,0 +1,2 @@ +export default class T { +} diff --git a/crates/swc_ecma_transforms_proposal/tests/decorators/issue-8631/1/output.ts b/crates/swc_ecma_transforms_proposal/tests/decorators/issue-8631/1/output.ts new file mode 100644 index 00000000000..847a117d073 --- /dev/null +++ b/crates/swc_ecma_transforms_proposal/tests/decorators/issue-8631/1/output.ts @@ -0,0 +1,28 @@ +var _initProto; +const decorate = (target: unknown, context: DecoratorContext)=>{ + console.log("decorated"); +}; +export class Color { + static{ + ({ e: [_initProto] } = _apply_decs_2203_r(this, [ + [ + decorate, + 3, + "rgba" + ] + ], [])); + } + constructor(hex: string); + constructor(r: number, g: number, b: number, a?: number); + constructor(r: string | number, g?: number, b?: number, a = 1){ + _initProto(this); + } + get rgba() { + return [ + 0, + 0, + 0, + 1 + ]; + } +} diff --git a/crates/swc_ecma_transforms_proposal/tests/decorators/issue-8631/options.json b/crates/swc_ecma_transforms_proposal/tests/decorators/issue-8631/options.json new file mode 100644 index 00000000000..4ad37b6e477 --- /dev/null +++ b/crates/swc_ecma_transforms_proposal/tests/decorators/issue-8631/options.json @@ -0,0 +1,3 @@ +{ + "plugins": [["proposal-decorators", { "version": "2022-03" }]] +}