diff options
-rw-r--r-- | integration/bunjs-only-snippets/atob.test.js | 3 | ||||
-rw-r--r-- | integration/bunjs-only-snippets/transpiler.test.js | 65 | ||||
-rw-r--r-- | src/js_parser/js_parser.zig | 44 |
3 files changed, 106 insertions, 6 deletions
diff --git a/integration/bunjs-only-snippets/atob.test.js b/integration/bunjs-only-snippets/atob.test.js index c146aa855..4945829e1 100644 --- a/integration/bunjs-only-snippets/atob.test.js +++ b/integration/bunjs-only-snippets/atob.test.js @@ -1,4 +1,4 @@ -import { expect, describe, it } from "bun:test"; +import { expect, it } from "bun:test"; function expectInvalidCharacters(val) { try { @@ -49,7 +49,6 @@ it("atob", () => { expectInvalidCharacters("=="); expectInvalidCharacters("==="); expectInvalidCharacters("===="); - expectInvalidCharacters("====="); }); diff --git a/integration/bunjs-only-snippets/transpiler.test.js b/integration/bunjs-only-snippets/transpiler.test.js index 2859c2b02..5553c1cc6 100644 --- a/integration/bunjs-only-snippets/transpiler.test.js +++ b/integration/bunjs-only-snippets/transpiler.test.js @@ -47,6 +47,71 @@ describe("Bun.Transpiler", () => { `; + it("JSX", () => { + var bun = new Bun.Transpiler({ + loader: "jsx", + }); + expect(bun.transformSync("export var foo = <div foo />")).toBe( + `export var foo = jsx("div", { + foo: true +}, undefined, false, undefined, this); +` + ); + expect(bun.transformSync("export var foo = <div foo={foo} />")).toBe( + `export var foo = jsx("div", { + foo +}, undefined, false, undefined, this); +` + ); + expect(bun.transformSync("export var foo = <div {...foo} />")).toBe( + `export var foo = jsx("div", { + ...foo +}, undefined, false, undefined, this); +` + ); + expect(bun.transformSync("export var hi = <div {foo} />")).toBe( + `export var hi = jsx("div", { + foo +}, undefined, false, undefined, this); +` + ); + try { + bun.transformSync("export var hi = <div {foo}={foo}= />"); + throw new Error("Expected error"); + } catch (e) { + expect(e.errors[0].message.includes('Expected ">"')).toBe(true); + } + + expect( + bun.transformSync("export var hi = <div {Foo}><Foo></Foo></div>") + ).toBe( + `export var hi = jsx("div", { + Foo, + children: jsx(Foo, {}, undefined, false, undefined, this) +}, undefined, false, undefined, this); +` + ); + expect( + bun.transformSync("export var hi = <div {Foo}><Foo></Foo></div>") + ).toBe( + `export var hi = jsx("div", { + Foo, + children: jsx(Foo, {}, undefined, false, undefined, this) +}, undefined, false, undefined, this); +` + ); + + expect(bun.transformSync("export var hi = <div>{123}}</div>").trim()).toBe( + `export var hi = jsx("div", { + children: [ + 123, + "}" + ] +}, undefined, true, undefined, this); + `.trim() + ); + }); + it("require with a dynamic non-string expression", () => { var nodeTranspiler = new Bun.Transpiler({ platform: "node" }); expect(nodeTranspiler.transformSync("require('hi' + bar)")).toBe( diff --git a/src/js_parser/js_parser.zig b/src/js_parser/js_parser.zig index 0ee6c5edf..b299b4498 100644 --- a/src/js_parser/js_parser.zig +++ b/src/js_parser/js_parser.zig @@ -11342,10 +11342,46 @@ fn NewParser_( defer i += 1; // Use Next() not ExpectInsideJSXElement() so we can parse "..." try p.lexer.next(); - try p.lexer.expect(.t_dot_dot_dot); - spread_prop_i = i; - spread_loc = p.lexer.loc(); - try props.append(G.Property{ .value = try p.parseExpr(.comma), .kind = .spread }); + + switch (p.lexer.token) { + .t_dot_dot_dot => { + try p.lexer.next(); + + spread_prop_i = i; + spread_loc = p.lexer.loc(); + try props.append(G.Property{ .value = try p.parseExpr(.comma), .kind = .spread }); + }, + // This implements + // <div {foo} /> + // -> + // <div foo={foo} /> + T.t_identifier => { + const name = p.lexer.identifier; + if (strings.eqlComptime(name, "let")) { + p.lexer.addError(p.source, p.lexer.loc(), "\"let\" is not allowed with JSX prop punning") catch unreachable; + } + const key = p.e(E.String{ .utf8 = name }, p.lexer.loc()); + const ref = p.storeNameInRef(name) catch unreachable; + const value = p.e(E.Identifier{ .ref = ref }, key.loc); + try p.lexer.next(); + + try props.append(G.Property{ .value = value, .key = key, .kind = .normal }); + }, + // This implements + // <div {"foo"} /> + // <div {'foo'} /> + // -> + // <div foo="foo" /> + // note: template literals are not supported, operations on strings are not supported either + T.t_string_literal => { + const key = p.e(p.lexer.toEString(), p.lexer.loc()); + try p.lexer.next(); + try props.append(G.Property{ .value = key, .key = key, .kind = .normal }); + }, + + else => try p.lexer.unexpected(), + } + try p.lexer.nextInsideJSXElement(); }, else => { |