aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2022-02-27 03:37:31 -0800
committerGravatar Jarred Sumner <jarred@jarredsumner.com> 2022-02-27 03:37:31 -0800
commitd91e8974130735fd43cd9c37d6147fe20f6b3290 (patch)
treebc0c4d03f3c2a066fe2d3be3c23e81bd3ab7fc7d
parent953ddef4820e7a0d6cc2e99a9ece5bc4127b6592 (diff)
downloadbun-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.js21
-rw-r--r--src/js_parser/js_parser.zig71
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