diff options
Diffstat (limited to 'integration/bunjs-only-snippets/transpiler.test.js')
-rw-r--r-- | integration/bunjs-only-snippets/transpiler.test.js | 468 |
1 files changed, 445 insertions, 23 deletions
diff --git a/integration/bunjs-only-snippets/transpiler.test.js b/integration/bunjs-only-snippets/transpiler.test.js index 7a768e756..315323e18 100644 --- a/integration/bunjs-only-snippets/transpiler.test.js +++ b/integration/bunjs-only-snippets/transpiler.test.js @@ -19,14 +19,8 @@ describe("Bun.Transpiler", () => { import { type xx } from 'mod'; import { type xx as yy } from 'mod'; import { type 'xx' as yy } from 'mod'; - import { type as } from 'mod'; - import { type as as } from 'mod'; - import { type as as as } from 'mod'; - import { type xx } from 'mod'; - import { type xx as yy } from 'mod'; import { type if as yy } from 'mod'; - import { type 'xx' as yy } from 'mod'; - import React, { type ReactNode, Component } from 'react'; + import React, { type ReactNode, Component as Romponent, Component } from 'react'; export const loader: LoaderFunction = async ({ @@ -46,14 +40,425 @@ describe("Bun.Transpiler", () => { console.log(params.postId); } + + + + `; describe("scanImports", () => { it("reports import paths, excluding types", () => { - const imports = transpiler.scanImports(code); + const imports = transpiler.scanImports(code, "tsx"); expect(imports.filter(({ path }) => path === "remix")).toHaveLength(1); expect(imports.filter(({ path }) => path === "mod")).toHaveLength(0); expect(imports.filter(({ path }) => path === "react")).toHaveLength(1); + expect(imports).toHaveLength(2); + }); + }); + + describe("parser", () => { + const parsed = (code, trim = true, autoExport = false) => { + if (autoExport) { + code = "export default (" + code + ")"; + } + + var out = transpiler.transformSync(code, "js"); + 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; + }; + + const expectPrinted = (code, out) => { + expect(parsed(code, true, true)).toBe(out); + }; + + const expectPrinted_ = (code, out) => { + expect(parsed(code, !out.endsWith(";\n"), false)).toBe(out); + }; + + const expectParseError = (code, message) => { + try { + parsed(code, false, false); + } catch (er) { + expect(er.message).toBe(message); + return; + } + + throw new Error("Expected parse error for code\n\t" + code); + }; + + // it("arrays", () => { + // expectPrinted("[]", "[]"); + // expectPrinted("[,]", "[,]"); + // expectPrinted("[1]", "[1]"); + // expectPrinted("[1,]", "[1]"); + // expectPrinted("[,1]", "[, 1]"); + // expectPrinted("[1,2]", "[1, 2]"); + // expectPrinted("[,1,2]", "[, 1, 2]"); + // expectPrinted("[1,,2]", "[1, , 2]"); + // expectPrinted("[1,2,]", "[1, 2]"); + // expectPrinted("[1,2,,]", "[1, 2, ,]"); + // }); + + // it("exponentiation", () => { + // expectPrinted("(delete x) ** 0", "(delete x) ** 0"); + // 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("(void x) ** 0", "(void x) ** 0"); + // expectPrinted("(typeof x) ** 0", "(typeof x) ** 0"); + // expectPrinted("(+x) ** 0", "(+x) ** 0"); + // expectPrinted("(-x) ** 0", "(-x) ** 0"); + // expectPrinted("(~x) ** 0", "(~x) ** 0"); + // expectPrinted("(!x) ** 0", "(!x) ** 0"); + // expectPrinted("(await x) ** 0", "(await x) ** 0"); + // expectPrinted("(await -x) ** 0", "(await -x) ** 0"); + + // expectPrinted("--x ** 2", "--x ** 2"); + // expectPrinted("++x ** 2", "++x ** 2"); + // expectPrinted("x-- ** 2", "x-- ** 2"); + // expectPrinted("x++ ** 2", "x++ ** 2"); + + // expectPrinted("(-x) ** 2", "(-x) ** 2"); + // expectPrinted("(+x) ** 2", "(+x) ** 2"); + // expectPrinted("(~x) ** 2", "(~x) ** 2"); + // expectPrinted("(!x) ** 2", "(!x) ** 2"); + // expectPrinted("(-1) ** 2", "(-1) ** 2"); + // expectPrinted("(+1) ** 2", "1 ** 2"); + // expectPrinted("(~1) ** 2", "(~1) ** 2"); + // expectPrinted("(!1) ** 2", "false ** 2"); + // expectPrinted("(void x) ** 2", "(void x) ** 2"); + // expectPrinted("(delete x) ** 2", "(delete x) ** 2"); + // expectPrinted("(typeof x) ** 2", "(typeof x) ** 2"); + // expectPrinted("undefined ** 2", "undefined ** 2"); + + // expectParseError("-x ** 2", "Unexpected **"); + // expectParseError("+x ** 2", "Unexpected **"); + // expectParseError("~x ** 2", "Unexpected **"); + // expectParseError("!x ** 2", "Unexpected **"); + // expectParseError("void x ** 2", "Unexpected **"); + // expectParseError("delete x ** 2", "Unexpected **"); + // expectParseError("typeof x ** 2", "Unexpected **"); + + // expectParseError("-x.y() ** 2", "Unexpected **"); + // expectParseError("+x.y() ** 2", "Unexpected **"); + // expectParseError("~x.y() ** 2", "Unexpected **"); + // expectParseError("!x.y() ** 2", "Unexpected **"); + // expectParseError("void x.y() ** 2", "Unexpected **"); + // expectParseError("delete x.y() ** 2", "Unexpected **"); + // expectParseError("typeof x.y() ** 2", "Unexpected **"); + + // expectParseError("delete x ** 0", "Unexpected **"); + // expectParseError("delete x.prop ** 0", "Unexpected **"); + // expectParseError("delete x[0] ** 0", "Unexpected **"); + // expectParseError("delete x?.prop ** 0", "Unexpected **"); + // expectParseError("void x ** 0", "Unexpected **"); + // expectParseError("typeof x ** 0", "Unexpected **"); + // expectParseError("+x ** 0", "Unexpected **"); + // expectParseError("-x ** 0", "Unexpected **"); + // expectParseError("~x ** 0", "Unexpected **"); + // expectParseError("!x ** 0", "Unexpected **"); + // expectParseError("await x ** 0", "Unexpected **"); + // expectParseError("await -x ** 0", "Unexpected **"); + // }); + + // it("await", () => { + // expectPrinted("await x", "await x"); + // expectPrinted("await +x", "await +x"); + // expectPrinted("await -x", "await -x"); + // expectPrinted("await ~x", "await ~x"); + // expectPrinted("await !x", "await !x"); + // expectPrinted("await --x", "await --x"); + // expectPrinted("await ++x", "await ++x"); + // expectPrinted("await x--", "await x--"); + // expectPrinted("await x++", "await x++"); + // expectPrinted("await void x", "await void x"); + // expectPrinted("await typeof x", "await typeof x"); + // expectPrinted("await (x * y)", "await (x * y)"); + // expectPrinted("await (x ** y)", "await (x ** y)"); + + // expectPrinted_( + // "async function f() { await delete x }", + // "async function f() {\n await delete x;\n}" + // ); + + // // expectParseError( + // // "await delete x", + // // "Delete of a bare identifier cannot be used in an ECMAScript module" + // // ); + // }); + + it.only("decls", () => { + // expectParseError("var x = 0", ""); + // expectParseError("let x = 0", ""); + // expectParseError("const x = 0", ""); + // expectParseError("for (var x = 0;;) ;", ""); + // expectParseError("for (let x = 0;;) ;", ""); + // expectParseError("for (const x = 0;;) ;", ""); + + // expectParseError("for (var x in y) ;", ""); + // expectParseError("for (let x in y) ;", ""); + // expectParseError("for (const x in y) ;", ""); + // expectParseError("for (var x of y) ;", ""); + // expectParseError("for (let x of y) ;", ""); + // expectParseError("for (const x of y) ;", ""); + + // expectParseError("var x", ""); + // expectParseError("let x", ""); + expectParseError("const x", 'The constant "x" must be initialized'); + expectParseError("const {}", "This constant must be initialized"); + expectParseError("const []", "This constant must be initialized"); + // expectParseError("for (var x;;) ;", ""); + // expectParseError("for (let x;;) ;", ""); + expectParseError( + "for (const x;;) ;", + 'The constant "x" must be initialized' + ); + expectParseError( + "for (const {};;) ;", + "This constant must be initialized" + ); + expectParseError( + "for (const [];;) ;", + "This constant must be initialized" + ); + + // Make sure bindings are visited during parsing + expectPrinted_("var {[x]: y} = {}", "var { [x]: y } = {}"); + expectPrinted_("var {...x} = {}", "var { ...x } = {}"); + + // Test destructuring patterns + expectPrinted_("var [...x] = []", "var [...x] = []"); + expectPrinted_("var {...x} = {}", "var { ...x } = {}"); + console.log("before"); + + expectPrinted_( + "export var foo = ([...x] = []) => {}", + "export var foo = ([...x] = []) => {\n}" + ); + + expectPrinted_( + "export var foo = ({...x} = {}) => {}", + "export var foo = ({ ...x } = {}) => {\n}" + ); + + expectParseError("var [...x,] = []", 'Unexpected "," after rest pattern'); + expectParseError("var {...x,} = {}", 'Unexpected "," after rest pattern'); + expectParseError( + "export default function() { return ([...x,] = []) => {} }", + "Unexpected trailing comma after rest element" + ); + expectParseError( + "({...x,} = {}) => {}", + "Unexpected trailing comma after rest element" + ); + + expectPrinted_("[b, ...c] = d", "[b, ...c] = d"); + expectPrinted_("([b, ...c] = d)", "[b, ...c] = d"); + expectPrinted_("({b, ...c} = d)", "({b, ...c } = d)"); + expectPrinted_("({a = b} = c)", "({a = b } = c)"); + expectPrinted_("({a: b = c} = d)", "({a: b = c } = d)"); + expectPrinted_("({a: b.c} = d)", "({a: b.c } = d)"); + expectPrinted_("[a = {}] = b", "[a = {}] = b"); + expectPrinted_("[[...a, b].x] = c", "[[...a, b].x] = c"); + expectPrinted_("[{...a, b}.x] = c", "[{...a, b }.x] = c"); + expectPrinted_("({x: [...a, b].x} = c)", "({x: [...a, b].x } = c)"); + expectPrinted_("({x: {...a, b}.x} = c)", "({x: {...a, b }.x } = c)"); + expectPrinted_("[x = [...a, b]] = c", "[x = [...a, b]] = c"); + expectPrinted_("[x = {...a, b}] = c", "[x = {...a, b }] = c"); + expectPrinted_("({x = [...a, b]} = c)", "({x = [...a, b] } = c)"); + expectPrinted_("({x = {...a, b}} = c)", "({ x = { ...a, b } } = c)"); + + expectPrinted_("(x = y)", "x = y"); + expectPrinted_("([] = [])", "[] = []"); + expectPrinted_("({} = {})", "({} = {})"); + expectPrinted_("([[]] = [[]])", "[[]] = [[]]"); + expectPrinted_("({x: {}} = {x: {}})", "({ x: {} } = { x: {} })"); + expectPrinted_("(x) = y", "x = y"); + expectParseError("([]) = []", "Invalid assignment target"); + expectParseError("({}) = {}", "Invalid assignment target"); + expectParseError("[([])] = [[]]", "Invalid assignment target"); + expectParseError("({x: ({})} = {x: {}})", "Invalid assignment target"); + expectParseError("(([]) = []) => {}", "Invalid binding pattern"); + expectParseError("(({}) = {}) => {}", "Invalid binding pattern"); + expectParseError( + "function f(([]) = []) {}", + 'Expected identifier but found "("\n' + ); + expectParseError( + "function f(({}) = {}) {}", + 'Expected identifier but found "("\n' + ); + + expectPrinted_("for (x in y) ;", "for (x in y)\n ;\n"); + expectPrinted_("for ([] in y) ;", "for ([] in y)\n ;\n"); + expectPrinted_("for ({} in y) ;", "for ({} in y)\n ;\n"); + expectPrinted_("for ((x) in y) ;", "for (x in y)\n ;\n"); + expectParseError("for (([]) in y) ;", "Invalid assignment target\n"); + expectParseError("for (({}) in y) ;", "Invalid assignment target\n"); + + expectPrinted_("for (x of y) ;", "for (x of y)\n ;\n"); + expectPrinted_("for ([] of y) ;", "for ([] of y)\n ;\n"); + expectPrinted_("for ({} of y) ;", "for ({} of y)\n ;\n"); + expectPrinted_("for ((x) of y) ;", "for (x of y)\n ;\n"); + expectParseError("for (([]) of y) ;", "Invalid assignment target\n"); + expectParseError("for (({}) of y) ;", "Invalid assignment target\n"); + + expectParseError( + "[[...a, b]] = c", + 'Unexpected "," after rest pattern\n' + ); + expectParseError( + "[{...a, b}] = c", + 'Unexpected "," after rest pattern\n' + ); + expectParseError( + "({x: [...a, b]} = c)", + 'Unexpected "," after rest pattern\n' + ); + expectParseError( + "({x: {...a, b}} = c)", + 'Unexpected "," after rest pattern\n' + ); + expectParseError("[b, ...c,] = d", 'Unexpected "," after rest pattern\n'); + expectParseError( + "([b, ...c,] = d)", + 'Unexpected "," after rest pattern\n' + ); + expectParseError( + "({b, ...c,} = d)", + 'Unexpected "," after rest pattern\n' + ); + expectParseError("({a = b})", 'Unexpected "="\n'); + expectParseError("({x = {a = b}} = c)", 'Unexpected "="\n'); + expectParseError("[a = {b = c}] = d", 'Unexpected "="\n'); + + expectPrinted_( + "for ([{a = {}}] in b) {}", + "for ([{ a = {} }] in b) {\n}\n" + ); + expectPrinted_( + "for ([{a = {}}] of b) {}", + "for ([{ a = {} }] of b) {\n}\n" + ); + expectPrinted_("for ({a = {}} in b) {}", "for ({ a = {} } in b) {\n}\n"); + expectPrinted_("for ({a = {}} of b) {}", "for ({ a = {} } of b) {\n}\n"); + + expectParseError("({a = {}} in b)", 'Unexpected "="\n'); + expectParseError("[{a = {}}]\nof()", 'Unexpected "="\n'); + expectParseError( + "for ([...a, b] in c) {}", + 'Unexpected "," after rest pattern\n' + ); + expectParseError( + "for ([...a, b] of c) {}", + 'Unexpected "," after rest pattern\n' + ); + }); + + it("regexp", () => { + expectPrinted("/x/g", "/x/g"); + expectPrinted("/x/i", "/x/i"); + expectPrinted("/x/m", "/x/m"); + expectPrinted("/x/s", "/x/s"); + expectPrinted("/x/u", "/x/u"); + expectPrinted("/x/y", "/x/y"); + expectPrinted("/gimme/g", "/gimme/g"); + expectPrinted("/gimgim/g", "/gimgim/g"); + + expectParseError( + "/x/msuygig", + 'Duplicate flag "g" in regular expression' + ); + }); + + 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") + + 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") + }); + }); + + describe("simplification", () => { + describe("type coercions", () => { + const dead = ` + if ("") { + TEST_FAIL + } + + if (false) { + TEST_FAIL + } + + if (0) { + TEST_FAIL + } + + if (void 0) { + TEST_FAIL + } + + if (null) { + TEST_FAIL + } + + var should_be_true = typeof "" === "string" || false + var should_be_false = typeof "" !== "string" && TEST_FAIL; + var should_be_false_2 = typeof true === "string" && TEST_FAIL; + var should_be_false_3 = typeof false === "string" && TEST_FAIL; + var should_be_false_4 = typeof 123n === "string" && TEST_FAIL; + var should_be_false_5 = typeof function(){} === "string" && TEST_FAIL; + var should_be_kept = typeof globalThis.BACON === "string" && TEST_OK; + var should_be_kept_1 = typeof TEST_OK === "string"; + + var should_be_kept_2 = TEST_OK ?? true; + var should_be_kept_4 = { "TEST_OK": true } ?? TEST_FAIL; + var should_be_false_6 = false ?? TEST_FAIL; + var should_be_true_7 = true ?? TEST_FAIL; + `; + const out = transpiler.transformSync(dead); + + for (let line of out.split("\n")) { + it(line, () => { + if (line.includes("should_be_kept")) { + expect(line.includes("TEST_OK")).toBe(true); + } + + if (line.includes("should_be_false")) { + if (!line.includes("= false")) + throw new Error(`Expected false in "${line}"`); + expect(line.includes("= false")).toBe(true); + } + + if (line.includes("TEST_FAIL")) { + throw new Error(`"${line}"\n\tshould not contain TEST_FAIL`); + } + }); + } }); }); @@ -64,8 +469,12 @@ describe("Bun.Transpiler", () => { expect(exports[0]).toBe("action"); expect(exports[2]).toBe("loader"); expect(exports[1]).toBe("default"); - expect(exports).toHaveLength(3); + + expect(imports.filter(({ path }) => path === "remix")).toHaveLength(1); + expect(imports.filter(({ path }) => path === "mod")).toHaveLength(0); + expect(imports.filter(({ path }) => path === "react")).toHaveLength(1); + expect(imports).toHaveLength(2); }); }); @@ -77,12 +486,14 @@ describe("Bun.Transpiler", () => { }/macro-check.js'; export default keepSecondArgument("Test failed", "Test passed"); + export function otherNamesStillWork() {} `); expect(out.includes("Test failed")).toBe(false); expect(out.includes("Test passed")).toBe(true); // ensure both the import and the macro function call are removed expect(out.includes("keepSecondArgument")).toBe(false); + expect(out.includes("otherNamesStillWork")).toBe(true); }); it("sync supports macros", () => { @@ -92,11 +503,15 @@ describe("Bun.Transpiler", () => { }/macro-check.js'; export default keepSecondArgument("Test failed", "Test passed"); + export function otherNamesStillWork() { + + } `); expect(out.includes("Test failed")).toBe(false); expect(out.includes("Test passed")).toBe(true); expect(out.includes("keepSecondArgument")).toBe(false); + expect(out.includes("otherNamesStillWork")).toBe(true); }); const importLines = [ @@ -106,18 +521,26 @@ describe("Bun.Transpiler", () => { describe("sync supports macros remap", () => { for (let importLine of importLines) { it(importLine, () => { - const out = transpiler.transformSync(` - ${importLine} - - export default bacon("Test failed", "Test passed"); - export function hi() { createElement("hi"); } - `); + var thisCode = ` + ${importLine} + + export default bacon("Test failed", "Test passed"); + export function otherNamesStillWork() { + return createElement("div"); + } + + `; + var out = transpiler.transformSync(thisCode); + try { + expect(out.includes("Test failed")).toBe(false); + expect(out.includes("Test passed")).toBe(true); - expect(out.includes("Test failed")).toBe(false); - expect(out.includes("Test passed")).toBe(true); - - expect(out.includes("bacon")).toBe(false); - expect(out.includes("createElement")).toBe(true); + expect(out.includes("bacon")).toBe(false); + expect(out.includes("createElement")).toBe(true); + } catch (e) { + console.log("Failing code:\n\n" + out + "\n"); + throw e; + } }); } }); @@ -125,7 +548,7 @@ describe("Bun.Transpiler", () => { it("macro remap removes import statement if its the only used one", () => { const out = transpiler.transformSync(` import {bacon} from 'react'; - + export default bacon("Test failed", "Test passed"); `); @@ -151,9 +574,8 @@ describe("Bun.Transpiler", () => { expect(out.includes("mod")).toBe(false); expect(out.includes("xx")).toBe(false); expect(out.includes("ReactNode")).toBe(false); - expect(out.includes("React")).toBe(true); - expect(out.includes("Component")).toBe(true); const { exports } = transpiler.scan(out); + exports.sort(); expect(exports[0]).toBe("action"); expect(exports[2]).toBe("loader"); |