diff options
author | 2022-02-27 03:37:31 -0800 | |
---|---|---|
committer | 2022-02-27 03:37:31 -0800 | |
commit | d91e8974130735fd43cd9c37d6147fe20f6b3290 (patch) | |
tree | bc0c4d03f3c2a066fe2d3be3c23e81bd3ab7fc7d | |
parent | 953ddef4820e7a0d6cc2e99a9ece5bc4127b6592 (diff) | |
download | bun-d91e8974130735fd43cd9c37d6147fe20f6b3290.tar.gz bun-d91e8974130735fd43cd9c37d6147fe20f6b3290.tar.zst bun-d91e8974130735fd43cd9c37d6147fe20f6b3290.zip |
[TS Parser] Implement `constructor(private foo)`
Input:
```
class Foo {
constructor(public bar: string = "baz") {}
bar: number;
}
```
Output:
```
class Foo {
bar;
constructor(bar = "baz") {
this.bar = bar;
}
baz;
}
```
-rw-r--r-- | integration/bunjs-only-snippets/transpiler.test.js | 21 | ||||
-rw-r--r-- | src/js_parser/js_parser.zig | 71 |
2 files changed, 91 insertions, 1 deletions
diff --git a/integration/bunjs-only-snippets/transpiler.test.js b/integration/bunjs-only-snippets/transpiler.test.js index f510cef74..34120e2d2 100644 --- a/integration/bunjs-only-snippets/transpiler.test.js +++ b/integration/bunjs-only-snippets/transpiler.test.js @@ -765,6 +765,27 @@ describe("Bun.Transpiler", () => { expectPrinted_("delete foo?.bar?.baz", "delete foo?.bar?.baz"); }); + it("useDefineForConst TypeScript class initialization", () => { + var { expectPrinted_ } = ts; + expectPrinted_( + ` +class Foo { + constructor(public x: string = "hey") {} + bar: number; +} +`.trim(), + ` +class Foo { + x; + constructor(x = "hey") { + this.x = x; + } + bar; +} +`.trim() + ); + }); + it("class static blocks", () => { expectPrinted_( "class Foo { static {} }", diff --git a/src/js_parser/js_parser.zig b/src/js_parser/js_parser.zig index 4c8591423..1425b7a58 100644 --- a/src/js_parser/js_parser.zig +++ b/src/js_parser/js_parser.zig @@ -4767,7 +4767,7 @@ fn NewParser_( while (true) { switch (p.lexer.token) { .t_identifier, .t_open_brace, .t_open_bracket => { - if (!js_lexer.TypeScriptAccessibilityModifier.has(p.lexer.identifier)) { + if (!js_lexer.TypeScriptAccessibilityModifier.has(text)) { break; } @@ -15180,6 +15180,7 @@ fn NewParser_( } var i: usize = 0; + var constructor_function: ?*E.Function = null; while (i < class.properties.len) : (i += 1) { var property = &class.properties[i]; @@ -15230,6 +15231,9 @@ fn NewParser_( // We need to explicitly assign the name to the property initializer if it // will be transformed such that it is no longer an inline initializer. + + var constructor_function_: ?*E.Function = null; + var name_to_keep: ?string = null; if (is_private) {} else if (!property.flags.is_method and !property.flags.is_computed) { if (property.key) |key| { @@ -15237,6 +15241,15 @@ fn NewParser_( name_to_keep = key.data.e_string.string(p.allocator) catch unreachable; } } + } else if (property.flags.is_method) { + if (comptime is_typescript_enabled) { + if (property.value.?.data == .e_function and property.key.?.data == .e_string and + property.key.?.data.e_string.eqlComptime("constructor")) + { + constructor_function_ = property.value.?.data.e_function; + constructor_function = constructor_function_; + } + } } if (property.value) |val| { @@ -15246,6 +15259,12 @@ fn NewParser_( } else { property.value = p.visitExpr(val); } + + if (comptime is_typescript_enabled) { + if (constructor_function_ != null and property.value != null and property.value.?.data == .e_function) { + constructor_function = property.value.?.data.e_function; + } + } } if (property.initializer) |val| { @@ -15259,6 +15278,56 @@ fn NewParser_( } } + // note: our version assumes useDefineForClassFields is true + if (comptime is_typescript_enabled) { + if (constructor_function) |constructor| { + var to_add: usize = 0; + for (constructor.func.args) |arg| { + to_add += @boolToInt(arg.is_typescript_ctor_field and arg.binding.data == .b_identifier); + } + + if (to_add > 0) { + // to match typescript behavior, we also must prepend to the class body + var stmts = std.ArrayList(Stmt).fromOwnedSlice(p.allocator, constructor.func.body.stmts); + stmts.ensureUnusedCapacity(to_add) catch unreachable; + var class_body = std.ArrayList(G.Property).fromOwnedSlice(p.allocator, class.properties); + class_body.ensureUnusedCapacity(to_add) catch unreachable; + var j: usize = 0; + + for (constructor.func.args) |arg| { + if (arg.is_typescript_ctor_field) { + switch (arg.binding.data) { + .b_identifier => |id| { + const name = p.symbols.items[id.ref.innerIndex()].original_name; + const ident = p.e(E.Identifier{ .ref = id.ref }, arg.binding.loc); + stmts.appendAssumeCapacity( + Expr.assignStmt( + p.e(E.Dot{ + .target = p.e(E.This{}, arg.binding.loc), + .name = name, + .name_loc = arg.binding.loc, + }, arg.binding.loc), + ident, + p.allocator, + ), + ); + // O(N) + class_body.items.len += 1; + std.mem.copyBackwards(G.Property, class_body.items[j + 1 .. class_body.items.len], class_body.items[j .. class_body.items.len - 1]); + class_body.items[j] = G.Property{ .key = ident }; + j += 1; + }, + else => {}, + } + } + } + + class.properties = class_body.toOwnedSlice(); + constructor.func.body.stmts = stmts.toOwnedSlice(); + } + } + } + if (!shadow_ref.eql(Ref.None)) { if (p.symbols.items[shadow_ref.innerIndex()].use_count_estimate == 0) { // Don't generate a shadowing name if one isn't needed |