diff options
author | 2022-02-27 01:53:51 -0800 | |
---|---|---|
committer | 2022-02-27 01:53:51 -0800 | |
commit | e9b4e5a3746db430d56e332b3e31cb87302f75de (patch) | |
tree | 2f7cc427444e0fc0c17427a92826d0fe317326df /integration/bunjs-only-snippets/transpiler.test.js | |
parent | 7576caa54a103ef652c7d8e9cda1585f00a2ab9b (diff) | |
download | bun-e9b4e5a3746db430d56e332b3e31cb87302f75de.tar.gz bun-e9b4e5a3746db430d56e332b3e31cb87302f75de.tar.zst bun-e9b4e5a3746db430d56e332b3e31cb87302f75de.zip |
[JS Parser] Improve test coverage
Diffstat (limited to 'integration/bunjs-only-snippets/transpiler.test.js')
-rw-r--r-- | integration/bunjs-only-snippets/transpiler.test.js | 417 |
1 files changed, 408 insertions, 9 deletions
diff --git a/integration/bunjs-only-snippets/transpiler.test.js b/integration/bunjs-only-snippets/transpiler.test.js index d4c3e6990..f510cef74 100644 --- a/integration/bunjs-only-snippets/transpiler.test.js +++ b/integration/bunjs-only-snippets/transpiler.test.js @@ -104,6 +104,55 @@ describe("Bun.Transpiler", () => { throw new Error("Expected parse error for code\n\t" + code); }; + const ts = { + parsed: (code, trim = true, autoExport = false) => { + if (autoExport) { + code = "export default (" + code + ")"; + } + + var out = transpiler.transformSync(code, "ts"); + if (autoExport && out.startsWith("export default ")) { + out = out.substring("export default ".length); + } + + if (trim) { + out = out.trim(); + + if (out.endsWith(";")) { + out = out.substring(0, out.length - 1); + } + + return out.trim(); + } + + return out; + }, + + expectPrinted: (code, out) => { + expect(ts.parsed(code, true, true)).toBe(out); + }, + + expectPrinted_: (code, out) => { + expect(ts.parsed(code, !out.endsWith(";\n"), false)).toBe(out); + }, + + expectParseError: (code, message) => { + try { + ts.parsed(code, false, false); + } catch (er) { + var err = er; + if (er instanceof AggregateError) { + err = err.errors[0]; + } + + expect(er.message).toBe(message); + + return; + } + + throw new Error("Expected parse error for code\n\t" + code); + }, + }; describe("parser", () => { it("arrays", () => { @@ -124,8 +173,7 @@ describe("Bun.Transpiler", () => { expectPrinted("(delete x.prop) ** 0", "(delete x.prop) ** 0"); expectPrinted("(delete x[0]) ** 0", "(delete x[0]) ** 0"); - // remember: we are printing from export default - expectPrinted("(delete x?.prop) ** 0", "(delete (x?.prop)) ** 0"); + expectPrinted("(delete x?.prop) ** 0", "(delete x?.prop) ** 0"); expectPrinted("(void x) ** 0", "(void x) ** 0"); expectPrinted("(typeof x) ** 0", "(typeof x) ** 0"); @@ -423,20 +471,371 @@ describe("Bun.Transpiler", () => { it("identifier escapes", () => { expectPrinted_("var _\u0076\u0061\u0072", "var _var"); - // expectParseError( - // "var \u0076\u0061\u0072", - // "Expected identifier but found \u0076\u0061\u0072" - // ); - // expectParseError(t, "\\u0076\\u0061\\u0072 foo", "<stdin>: ERROR: Unexpected \"\\\\u0076\\\\u0061\\\\u0072\"\n") + expectParseError( + "var \u0076\u0061\u0072", + 'Expected identifier but found "\u0076\u0061\u0072"' + ); + expectParseError( + "\\u0076\\u0061\\u0072 foo", + "Unexpected \\u0076\\u0061\\u0072" + ); expectPrinted_("foo._\u0076\u0061\u0072", "foo._var"); expectPrinted_("foo.\u0076\u0061\u0072", "foo.var"); - // expectParseError(t, "\u200Ca", "<stdin>: ERROR: Unexpected \"\\u200c\"\n") - // expectParseError(t, "\u200Da", "<stdin>: ERROR: Unexpected \"\\u200d\"\n") + // expectParseError("\u200Ca", 'Unexpected "\\u200c"'); + // expectParseError("\u200Da", 'Unexpected "\\u200d"'); }); }); + it("private identifiers", () => { + expectParseError("#foo", "Unexpected #foo"); + expectParseError("#foo in this", "Unexpected #foo"); + expectParseError("this.#foo", 'Expected identifier but found "#foo"'); + expectParseError("this?.#foo", 'Expected identifier but found "#foo"'); + expectParseError("({ #foo: 1 })", 'Expected identifier but found "#foo"'); + expectParseError( + "class Foo { x = { #foo: 1 } }", + 'Expected identifier but found "#foo"' + ); + expectParseError("class Foo { x = #foo }", 'Expected "in" but found "}"'); + expectParseError( + "class Foo { #foo; foo() { delete this.#foo } }", + 'Deleting the private name "#foo" is forbidden' + ); + expectParseError( + "class Foo { #foo; foo() { delete this?.#foo } }", + 'Deleting the private name "#foo" is forbidden' + ); + expectParseError( + "class Foo extends Bar { #foo; foo() { super.#foo } }", + "Parse error" + // 'Expected identifier but found "#foo"' + ); + expectParseError( + "class Foo { #foo = () => { for (#foo in this) ; } }", + "Unexpected #foo" + ); + expectParseError( + "class Foo { #foo = () => { for (x = #foo in this) ; } }", + "Unexpected #foo" + ); + expectPrinted_("class Foo { #foo }", "class Foo {\n #foo;\n}"); + expectPrinted_("class Foo { #foo = 1 }", "class Foo {\n #foo = 1;\n}"); + expectPrinted_( + "class Foo { #foo = #foo in this }", + "class Foo {\n #foo = #foo in this;\n}" + ); + expectPrinted_( + "class Foo { #foo = #foo in (#bar in this); #bar }", + "class Foo {\n #foo = #foo in (#bar in this);\n #bar;\n}" + ); + expectPrinted_( + "class Foo { #foo() {} }", + "class Foo {\n #foo() {\n }\n}" + ); + expectPrinted_( + "class Foo { get #foo() {} }", + "class Foo {\n get #foo() {\n }\n}" + ); + expectPrinted_( + "class Foo { set #foo(x) {} }", + "class Foo {\n set #foo(x) {\n }\n}" + ); + expectPrinted_( + "class Foo { static #foo }", + "class Foo {\n static #foo;\n}" + ); + expectPrinted_( + "class Foo { static #foo = 1 }", + "class Foo {\n static #foo = 1;\n}" + ); + expectPrinted_( + "class Foo { static #foo() {} }", + "class Foo {\n static #foo() {\n }\n}" + ); + expectPrinted_( + "class Foo { static get #foo() {} }", + "class Foo {\n static get #foo() {\n }\n}" + ); + expectPrinted_( + "class Foo { static set #foo(x) {} }", + "class Foo {\n static set #foo(x) {\n }\n}" + ); + + expectParseError( + "class Foo { #foo = #foo in #bar in this; #bar }", + "Unexpected #bar" + ); + + expectParseError( + "class Foo { #constructor }", + 'Invalid field name "#constructor"' + ); + expectParseError( + "class Foo { #constructor() {} }", + 'Invalid method name "#constructor"' + ); + expectParseError( + "class Foo { static #constructor }", + 'Invalid field name "#constructor"' + ); + expectParseError( + "class Foo { static #constructor() {} }", + 'Invalid method name "#constructor"' + ); + expectParseError( + "class Foo { #\\u0063onstructor }", + 'Invalid field name "#constructor"' + ); + expectParseError( + "class Foo { #\\u0063onstructor() {} }", + 'Invalid method name "#constructor"' + ); + expectParseError( + "class Foo { static #\\u0063onstructor }", + 'Invalid field name "#constructor"' + ); + expectParseError( + "class Foo { static #\\u0063onstructor() {} }", + 'Invalid method name "#constructor"' + ); + const errorText = '"#foo" has already been declared'; + expectParseError("class Foo { #foo; #foo }", errorText); + expectParseError("class Foo { #foo; static #foo }", errorText); + expectParseError("class Foo { static #foo; #foo }", errorText); + expectParseError("class Foo { #foo; #foo() {} }", errorText); + expectParseError("class Foo { #foo; get #foo() {} }", errorText); + expectParseError("class Foo { #foo; set #foo(x) {} }", errorText); + expectParseError("class Foo { #foo() {} #foo }", errorText); + expectParseError("class Foo { get #foo() {} #foo }", errorText); + expectParseError("class Foo { set #foo(x) {} #foo }", errorText); + expectParseError("class Foo { get #foo() {} get #foo() {} }", errorText); + expectParseError("class Foo { set #foo(x) {} set #foo(x) {} }", errorText); + expectParseError( + "class Foo { get #foo() {} set #foo(x) {} #foo }", + errorText + ); + expectParseError( + "class Foo { set #foo(x) {} get #foo() {} #foo }", + errorText + ); + + expectPrinted_( + "class Foo { get #foo() {} set #foo(x) { this.#foo } }", + "class Foo {\n get #foo() {\n }\n set #foo(x) {\n this.#foo;\n }\n}" + ); + expectPrinted_( + "class Foo { set #foo(x) { this.#foo } get #foo() {} }", + "class Foo {\n set #foo(x) {\n this.#foo;\n }\n get #foo() {\n }\n}" + ); + expectPrinted_( + "class Foo { #foo } class Bar { #foo }", + "class Foo {\n #foo;\n}\n\nclass Bar {\n #foo;\n}" + ); + expectPrinted_( + "class Foo { foo = this.#foo; #foo }", + "class Foo {\n foo = this.#foo;\n #foo;\n}" + ); + expectPrinted_( + "class Foo { foo = this?.#foo; #foo }", + "class Foo {\n foo = this?.#foo;\n #foo;\n}" + ); + expectParseError( + "class Foo { #foo } class Bar { foo = this.#foo }", + 'Private name "#foo" must be declared in an enclosing class' + ); + expectParseError( + "class Foo { #foo } class Bar { foo = this?.#foo }", + 'Private name "#foo" must be declared in an enclosing class' + ); + expectParseError( + "class Foo { #foo } class Bar { foo = #foo in this }", + 'Private name "#foo" must be declared in an enclosing class' + ); + + expectPrinted_( + `class Foo { + #if + #im() { return this.#im(this.#if) } + static #sf + static #sm() { return this.#sm(this.#sf) } + foo() { + return class { + #inner() { + return [this.#im, this?.#inner, this?.x.#if] + } + } + } +} +`, + `class Foo { + #if; + #im() { + return this.#im(this.#if); + } + static #sf; + static #sm() { + return this.#sm(this.#sf); + } + foo() { + return class { + #inner() { + return [this.#im, this?.#inner, this?.x.#if]; + } + }; + } +}` + ); + }); + + it("type only exports", () => { + let { expectPrinted_, expectParseError } = ts; + expectPrinted_("export type {foo, bar as baz} from 'bar'", ""); + expectPrinted_("export type {foo, bar as baz}", ""); + expectPrinted_("export type {foo} from 'bar'; x", "x"); + expectPrinted_("export type {foo} from 'bar'\nx", "x"); + expectPrinted_("export type {default} from 'bar'", ""); + expectPrinted_( + "export { type } from 'mod'; type", + 'export { type } from "mod";\ntype' + ); + expectPrinted_( + "export { type, as } from 'mod'", + 'export { type, as } from "mod"' + ); + expectPrinted_( + "export { x, type foo } from 'mod'; x", + 'export { x } from "mod";\nx' + ); + expectPrinted_( + "export { x, type as } from 'mod'; x", + 'export { x } from "mod";\nx' + ); + expectPrinted_( + "export { x, type foo as bar } from 'mod'; x", + 'export { x } from "mod";\nx' + ); + expectPrinted_( + "export { x, type foo as as } from 'mod'; x", + 'export { x } from "mod";\nx' + ); + expectPrinted_( + "export { type as as } from 'mod'; as", + 'export { type as as } from "mod";\nas' + ); + expectPrinted_( + "export { type as foo } from 'mod'; foo", + 'export { type as foo } from "mod";\nfoo' + ); + expectPrinted_( + "export { type as type } from 'mod'; type", + 'export { type } from "mod";\ntype' + ); + expectPrinted_( + "export { x, type as as foo } from 'mod'; x", + 'export { x } from "mod";\nx' + ); + expectPrinted_( + "export { x, type as as as } from 'mod'; x", + 'export { x } from "mod";\nx' + ); + expectPrinted_( + "export { x, type type as as } from 'mod'; x", + 'export { x } from "mod";\nx' + ); + expectPrinted_( + "export { x, \\u0074ype y }; let x, y", + "export { x };\nlet x, y" + ); + expectPrinted_( + "export { x, \\u0074ype y } from 'mod'", + 'export { x } from "mod"' + ); + expectPrinted_( + "export { x, type if } from 'mod'", + 'export { x } from "mod"' + ); + expectPrinted_("export { x, type y as if }; let x", "export { x };\nlet x"); + }); + + it("delete + optional chain", () => { + expectPrinted_("delete foo.bar.baz", "delete foo.bar.baz"); + expectPrinted_("delete foo?.bar.baz", "delete foo?.bar.baz"); + expectPrinted_("delete foo?.bar?.baz", "delete foo?.bar?.baz"); + }); + + it("class static blocks", () => { + expectPrinted_( + "class Foo { static {} }", + "class Foo {\n static {\n }\n}" + ); + expectPrinted_( + "class Foo { static {} x = 1 }", + "class Foo {\n static {\n }\n x = 1;\n}" + ); + expectPrinted_( + "class Foo { static { this.foo() } }", + "class Foo {\n static {\n this.foo();\n }\n}" + ); + + expectParseError( + "class Foo { static { yield } }", + '"yield" is a reserved word and cannot be used in strict mode' + ); + expectParseError( + "class Foo { static { await } }", + 'The keyword "await" cannot be used here' + ); + expectParseError( + "class Foo { static { return } }", + "A return statement cannot be used here" + ); + expectParseError( + "class Foo { static { break } }", + 'Cannot use "break" here' + ); + expectParseError( + "class Foo { static { continue } }", + 'Cannot use "continue" here' + ); + expectParseError( + "x: { class Foo { static { break x } } }", + 'There is no containing label named "x"' + ); + expectParseError( + "x: { class Foo { static { continue x } } }", + 'There is no containing label named "x"' + ); + + expectParseError( + "class Foo { get #x() { this.#x = 1 } }", + 'Writing to getter-only property "#x" will throw' + ); + expectParseError( + "class Foo { get #x() { this.#x += 1 } }", + 'Writing to getter-only property "#x" will throw' + ); + expectParseError( + "class Foo { set #x(x) { this.#x } }", + 'Reading from setter-only property "#x" will throw' + ); + expectParseError( + "class Foo { set #x(x) { this.#x += 1 } }", + 'Reading from setter-only property "#x" will throw' + ); + + // Writing to method warnings + expectParseError( + "class Foo { #x() { this.#x = 1 } }", + 'Writing to read-only method "#x" will throw' + ); + expectParseError( + "class Foo { #x() { this.#x += 1 } }", + 'Writing to read-only method "#x" will throw' + ); + }); + describe("simplification", () => { it("constant folding", () => { expectPrinted("1 && 2", "2"); |