aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar dave caruso <me@paperdave.net> 2023-04-15 21:28:21 -0400
committerGravatar GitHub <noreply@github.com> 2023-04-15 18:28:21 -0700
commit2dc3f4e0306518b16eb0bd9a505f9bc12963ec4d (patch)
treef8c1bed26c5006560f05b3feb261e0377eb20afa
parent1a7c3cdf3b637c0770b2bec09280a0edb46d328c (diff)
downloadbun-2dc3f4e0306518b16eb0bd9a505f9bc12963ec4d.tar.gz
bun-2dc3f4e0306518b16eb0bd9a505f9bc12963ec4d.tar.zst
bun-2dc3f4e0306518b16eb0bd9a505f9bc12963ec4d.zip
more work on bundler tests (#2667)
* Fix `make dev` reusing old .o file * more bundler tests * document capture * bundler tests * bundler tests * bundler tests * finish up * remove bad thing to merge on main
-rw-r--r--Makefile2
-rw-r--r--test/bundler/bundler_cjs2esm.test.ts20
-rw-r--r--test/bundler/bundler_edgecase.test.ts56
-rw-r--r--test/bundler/bundler_minify.test.ts54
-rw-r--r--test/bundler/esbuild/css.test.ts6
-rw-r--r--test/bundler/esbuild/dce.test.ts2
-rw-r--r--test/bundler/esbuild/default.test.ts63
-rw-r--r--test/bundler/esbuild/importstar.test.ts4
-rw-r--r--test/bundler/esbuild/importstar_ts.test.ts4
-rw-r--r--test/bundler/esbuild/loader.test.ts7
-rw-r--r--test/bundler/esbuild/lower.test.ts12
-rw-r--r--test/bundler/esbuild/packagejson.test.ts12
-rw-r--r--test/bundler/esbuild/splitting.test.ts20
-rw-r--r--test/bundler/esbuild/ts.test.ts658
-rw-r--r--test/bundler/expectBundled.md20
-rw-r--r--test/bundler/expectBundled.ts95
-rw-r--r--test/bundler/report-bundler-test-progress.sh2
-rwxr-xr-xtest/bundler/run-single-bundler-test.sh4
18 files changed, 826 insertions, 215 deletions
diff --git a/Makefile b/Makefile
index d2d577460..05deffc10 100644
--- a/Makefile
+++ b/Makefile
@@ -1084,7 +1084,7 @@ dev-obj-linux:
dev: mkdir-dev dev-obj bun-link-lld-debug
mkdir-dev:
- mkdir -p $(DEBUG_PACKAGE_DIR)/bin
+ mkdir -p $(DEBUG_PACKAGE_DIR)
test-all:
$(RELEASE_BUN) test
diff --git a/test/bundler/bundler_cjs2esm.test.ts b/test/bundler/bundler_cjs2esm.test.ts
index 4c37e9591..ffe9a98f8 100644
--- a/test/bundler/bundler_cjs2esm.test.ts
+++ b/test/bundler/bundler_cjs2esm.test.ts
@@ -4,5 +4,23 @@ import { bundlerTest, expectBundled, itBundled, testForFile } from "./expectBund
var { describe, test, expect } = testForFile(import.meta.path);
describe("bundler", () => {
- return;
+ // TODO: I must be misunderstanding how the cjs to esm transforms work. since this should pass
+ itBundled("cjs2esm/ModuleExportsFunction", {
+ files: {
+ "/entry.js": /* js */ `
+ import { foo } from 'lib';
+ console.log(foo());
+ `,
+ "/node_modules/lib/index.js": /* js */ `
+ module.exports.foo = function() {
+ return 'foo';
+ }
+ `,
+ },
+ minifySyntax: true,
+ platform: "bun",
+ onAfterBundle(api) {
+ assert(!api.readFile("/out.js").includes("__commonJS"), "should not include the commonJS helper");
+ },
+ });
});
diff --git a/test/bundler/bundler_edgecase.test.ts b/test/bundler/bundler_edgecase.test.ts
index cb8a90b81..98c8c0a8e 100644
--- a/test/bundler/bundler_edgecase.test.ts
+++ b/test/bundler/bundler_edgecase.test.ts
@@ -37,18 +37,50 @@ describe("bundler", () => {
},
run: true,
});
- // itBundled("edgecase/PureCommentInLineComment", {
- // files: {
- // "/entry.js": /* js */ `
- // (function () {
- // // Some text that contains a pure comment in it like /* @__PURE__ */, with other text around it.
+ itBundled("edgecase/BunPluginTreeShakeImport", {
+ // This only appears at runtime and not with bun build, even with --transform
+ files: {
+ "/entry.ts": /* js */ `
+ import { A, B } from "./somewhere-else";
+ import { plugin } from "bun";
- // // console.log;
+ plugin(B());
- // fn2("TODO: should this call be kept?");
- // })();
- // `,
- // },
- // dce: true,
- // });
+ new A().chainedMethods();
+ `,
+ "/somewhere-else.ts": /* js */ `
+ export class A {
+ chainedMethods() {
+ console.log("hey");
+ }
+ }
+ export function B() {
+ return { name: 'hey' }
+ }
+ `,
+ },
+ external: ["external"],
+ mode: "transform",
+ minifySyntax: true,
+ platform: "bun",
+ run: { file: "/entry.ts" },
+ });
+ itBundled("edgecase/TemplateStringIssue622", {
+ files: {
+ "/entry.ts": /* js */ `
+ capture(\`\\?\`);
+ capture(hello\`\\?\`);
+ `,
+ },
+ capture: ["`\\\\?`", "hello`\\\\?`"],
+ platform: "bun",
+ });
+ itBundled("edgecase/StringNullBytes", {
+ files: {
+ "/entry.ts": /* js */ `
+ capture("Hello\0");
+ `,
+ },
+ capture: ['"Hello\0"'],
+ });
});
diff --git a/test/bundler/bundler_minify.test.ts b/test/bundler/bundler_minify.test.ts
new file mode 100644
index 000000000..7eacfbde6
--- /dev/null
+++ b/test/bundler/bundler_minify.test.ts
@@ -0,0 +1,54 @@
+import { describe } from "bun:test";
+import { itBundled } from "./expectBundled";
+
+describe("bundler", () => {
+ itBundled("minify/TemplateStringFolding", {
+ files: {
+ "/entry.js": /* js */ `
+ capture(\`\${1}-\${2}-\${3}-\${null}-\${undefined}-\${true}-\${false}\`);
+ capture(\`\\uD83D\\uDE0B \\uD83D\\uDCCB \\uD83D\\uDC4C\`.length)
+ capture(\`\\uD83D\\uDE0B \\uD83D\\uDCCB \\uD83D\\uDC4C\`.length === 8)
+ capture(\`\\uD83D\\uDE0B \\uD83D\\uDCCB \\uD83D\\uDC4C\`.length == 8)
+ capture(\`\\uD83D\\uDE0B \\uD83D\\uDCCB \\uD83D\\uDC4C\`.length === 1)
+ capture(\`\\uD83D\\uDE0B \\uD83D\\uDCCB \\uD83D\\uDC4C\`.length == 1)
+ capture("\\uD83D\\uDE0B \\uD83D\\uDCCB \\uD83D\\uDC4C".length)
+ capture("\\uD83D\\uDE0B \\uD83D\\uDCCB \\uD83D\\uDC4C".length === 8)
+ capture("\\uD83D\\uDE0B \\uD83D\\uDCCB \\uD83D\\uDC4C".length == 8)
+ capture("\\uD83D\\uDE0B \\uD83D\\uDCCB \\uD83D\\uDC4C".length === 1)
+ capture("\\uD83D\\uDE0B \\uD83D\\uDCCB \\uD83D\\uDC4C".length == 1)
+ capture('\\uD83D\\uDE0B \\uD83D\\uDCCB \\uD83D\\uDC4C'.length)
+ capture('\\uD83D\\uDE0B \\uD83D\\uDCCB \\uD83D\\uDC4C'.length === 8)
+ capture('\\uD83D\\uDE0B \\uD83D\\uDCCB \\uD83D\\uDC4C'.length == 8)
+ capture('\\uD83D\\uDE0B \\uD83D\\uDCCB \\uD83D\\uDC4C'.length === 1)
+ capture('\\uD83D\\uDE0B \\uD83D\\uDCCB \\uD83D\\uDC4C'.length == 1)
+ capture(\`😋📋👌\`.length === 6)
+ capture(\`😋📋👌\`.length == 6)
+ capture(\`😋📋👌\`.length === 2)
+ capture(\`😋📋👌\`.length == 2)
+ `,
+ },
+ capture: [
+ '"1-2-3-null-undefined-true-false"',
+ "8",
+ "true",
+ "true",
+ "false",
+ "false",
+ "8",
+ "true",
+ "true",
+ "false",
+ "false",
+ "8",
+ "true",
+ "true",
+ "false",
+ "false",
+ "true",
+ "true",
+ "false",
+ "false",
+ ],
+ platform: "bun",
+ });
+});
diff --git a/test/bundler/esbuild/css.test.ts b/test/bundler/esbuild/css.test.ts
index 2e0798082..83203cb6c 100644
--- a/test/bundler/esbuild/css.test.ts
+++ b/test/bundler/esbuild/css.test.ts
@@ -350,7 +350,7 @@ var { describe, test, expect } = testForFile(import.meta.path);
// /* TODO FIX expectedScanLog: `entry.css: ERROR: Bundling with conditional "@import" rules is not currently supported
// `, */
// });
-// itBundled("css/CSSAndJavaScriptCodeSplittingIssue1064", {
+// itBundled("css/CSSAndJavaScriptCodeSplittingESBuildIssue1064", {
// // GENERATED
// files: {
// "/a.js": /* js */ `
@@ -376,7 +376,7 @@ var { describe, test, expect } = testForFile(import.meta.path);
// format: "esm",
// splitting: true,
// });
-// itBundled("css/CSSExternalQueryAndHashNoMatchIssue1822", {
+// itBundled("css/CSSExternalQueryAndHashNoMatchESBuildIssue1822", {
// // GENERATED
// files: {
// "/entry.css": /* css */ `
@@ -391,7 +391,7 @@ var { describe, test, expect } = testForFile(import.meta.path);
// NOTE: You can mark the path "foo/bar.png#baz" as external to exclude it from the bundle, which will remove this error.
// `, */
// });
-// itBundled("css/CSSExternalQueryAndHashMatchIssue1822", {
+// itBundled("css/CSSExternalQueryAndHashMatchESBuildIssue1822", {
// // GENERATED
// files: {
// "/entry.css": /* css */ `
diff --git a/test/bundler/esbuild/dce.test.ts b/test/bundler/esbuild/dce.test.ts
index 8b3e2c289..4ba4f4b48 100644
--- a/test/bundler/esbuild/dce.test.ts
+++ b/test/bundler/esbuild/dce.test.ts
@@ -641,7 +641,7 @@ describe("bundler", () => {
stdout: "1",
},
});
- itBundled("dce/PackageJsonSideEffectsFalseNoWarningInNodeModulesIssue999", {
+ itBundled("dce/PackageJsonSideEffectsFalseNoWarningInNodeModulesESBuildIssue999", {
files: {
"/Users/user/project/src/entry.js": /* js */ `
import "demo-pkg"
diff --git a/test/bundler/esbuild/default.test.ts b/test/bundler/esbuild/default.test.ts
index 93bc21492..5ee1c0f1c 100644
--- a/test/bundler/esbuild/default.test.ts
+++ b/test/bundler/esbuild/default.test.ts
@@ -1,6 +1,13 @@
import assert from "assert";
import dedent from "dedent";
-import { ESBUILD_PATH, bundlerTest, expectBundled, itBundled, testForFile } from "../expectBundled";
+import {
+ ESBUILD_PATH,
+ RUN_UNCHECKED_TESTS,
+ bundlerTest,
+ expectBundled,
+ itBundled,
+ testForFile,
+} from "../expectBundled";
var { describe, test, expect } = testForFile(import.meta.path);
// Tests ported from:
@@ -1012,27 +1019,39 @@ describe("bundler", () => {
stdout: "./test.txt",
},
});
- itBundled("default/RequireWithoutCall", {
- // TODO: MANUAL CHECK: `require` on line one has to be renamed to `__require`
+ itBundled("default/RequireWithoutCallPlatformNeutral", {
+ // `require` on line one has to be renamed to `__require`
files: {
"/entry.js": /* js */ `
const req = require
req('./entry')
+ capture(req)
`,
},
platform: "neutral",
+ onAfterBundle(api) {
+ const varName = api.captureFile("/out.js")[0];
+ const assignmentValue = api.readFile("/out.js").match(new RegExp(`${varName} = (.*);`))![1];
+ expect(assignmentValue).not.toBe("require");
+ },
});
- itBundled("default/NestedRequireWithoutCall", {
- // TODO: MANUAL CHECK: `require` on line one has to be renamed to `__require`
+ itBundled("default/NestedRequireWithoutCallPlatformNeutral", {
+ // `require` on line one has to be renamed to `__require`
files: {
"/entry.js": /* js */ `
(() => {
const req = require
req('./entry')
+ capture(req)
})()
`,
},
platform: "neutral",
+ onAfterBundle(api) {
+ const varName = api.captureFile("/out.js")[0];
+ const assignmentValue = api.readFile("/out.js").match(new RegExp(`${varName} = (.*);`))![1];
+ expect(assignmentValue).not.toBe("require");
+ },
});
itBundled("default/RequireWithCallInsideTry", {
files: {
@@ -1067,7 +1086,7 @@ describe("bundler", () => {
run: [{ file: "/test1.js" }, { file: "/test2.js" }],
});
itBundled("default/RequireWithoutCallInsideTry", {
- // TODO: MANUAL CHECK: `require` on line one has to be renamed to `__require`
+ // `require` has to be renamed to `__require`
files: {
"/entry.js": /* js */ `
try {
@@ -1075,10 +1094,16 @@ describe("bundler", () => {
var aliasedRequire = require;
aliasedRequire('./locale/' + name);
getSetGlobalLocale(oldLocale);
+ capture(aliasedRequire)
} catch (e) {}
`,
},
platform: "neutral",
+ onAfterBundle(api) {
+ const varName = api.captureFile("/out.js")[0];
+ const assignmentValue = api.readFile("/out.js").match(new RegExp(`${varName} = (.*);`))![1];
+ expect(assignmentValue).not.toBe("require");
+ },
});
itBundled("default/RequirePropertyAccessCommonJS", {
files: {
@@ -1886,10 +1911,6 @@ describe("bundler", () => {
outfile: "/out.js",
minifyIdentifiers: true,
mode: "transform",
- run: {
- // TODO: use bun here after https://github.com/oven-sh/bun/issues/1724 is fixed
- runtime: "node",
- },
});
itBundled("default/WithStatementTaintingNoBundle", {
// TODO: MANUAL CHECK: make sure the snapshot we use works.
@@ -1977,7 +1998,7 @@ describe("bundler", () => {
assert(!text.includes("renameMe"), "Should have renamed all `renameMe` variabled");
},
});
- itBundled("default/ImportReExportES6Issue149", {
+ itBundled("default/ImportReExportES6ESBuildIssue149", {
files: {
"/app.jsx": /* jsx */ `
import { p as Part, h, render } from './import';
@@ -2405,7 +2426,7 @@ describe("bundler", () => {
stdout: '[{},{},{"foo":123},{"bar":123}] true',
},
});
- itBundled("default/EmptyExportClauseBundleAsCommonJSIssue910", {
+ itBundled("default/EmptyExportClauseBundleAsCommonJSESBuildIssue910", {
files: {
"/entry.js": `console.log(JSON.stringify(require('./types.mjs')))`,
"/types.mjs": `export {}`,
@@ -2432,7 +2453,7 @@ describe("bundler", () => {
assert(api.readFile("/out.js").includes('"use strict";'), '"use strict"; was emitted');
},
});
- itBundled("default/UseStrictDirectiveBundleIssue1837", {
+ itBundled("default/UseStrictDirectiveBundleESBuildIssue1837", {
files: {
"/entry.js": /* js */ `
const p = require('./cjs').foo;
@@ -2453,7 +2474,7 @@ describe("bundler", () => {
stdout: "function",
},
});
- itBundled("default/UseStrictDirectiveBundleIIFEIssue2264", {
+ itBundled("default/UseStrictDirectiveBundleIIFEESBuildIssue2264", {
files: {
"/entry.js": /* js */ `
'use strict'
@@ -2465,7 +2486,7 @@ describe("bundler", () => {
assert(api.readFile("/out.js").includes('"use strict";'), '"use strict"; should be emitted');
},
});
- itBundled("default/UseStrictDirectiveBundleCJSIssue2264", {
+ itBundled("default/UseStrictDirectiveBundleCJSESBuildIssue2264", {
files: {
"/entry.js": /* js */ `
'use strict'
@@ -2477,7 +2498,7 @@ describe("bundler", () => {
assert(api.readFile("/out.js").includes('"use strict";'), '"use strict"; should be emitted');
},
});
- itBundled("default/UseStrictDirectiveBundleESMIssue2264", {
+ itBundled("default/UseStrictDirectiveBundleESMESBuildIssue2264", {
files: {
"/entry.js": /* js */ `
'use strict'
@@ -4222,7 +4243,7 @@ describe("bundler", () => {
stdout: `[[1,1,1],[1,1,1],[2,2,2,null,null]]`,
},
});
- itBundled("default/DefineInfiniteLoopIssue2407", {
+ itBundled("default/DefineInfiniteLoopESBuildIssue2407", {
files: {
"/entry.js": /* js */ `
a.b()
@@ -4568,7 +4589,7 @@ describe("bundler", () => {
// mode: "transform",
// external: ["a", "b", "c", "react/jsx-dev-runtime"],
// });
- return;
+ if (!RUN_UNCHECKED_TESTS) return;
// I cant get bun to use `this` as the JSX runtime. It's a pretty silly idea anyways.
itBundled("default/JSXThisValueCommonJS", {
files: {
@@ -4999,7 +5020,7 @@ describe("bundler", () => {
},
external: ["some-path"],
});
- itBundled("default/StrictModeNestedFnDeclKeepNamesVariableInliningIssue1552", {
+ itBundled("default/StrictModeNestedFnDeclKeepNamesVariableInliningESBuildIssue1552", {
// GENERATED
files: {
"/entry.js": /* js */ `
@@ -5784,7 +5805,7 @@ describe("bundler", () => {
b.js: NOTE: Another definition of "x" comes from "b.js" here:
`, */
});
- itBundled("default/NonDeterminismIssue2537", {
+ itBundled("default/NonDeterminismESBuildIssue2537", {
// GENERATED
files: {
"/entry.ts": /* ts */ `
@@ -6327,7 +6348,7 @@ describe("bundler", () => {
`,
},
});
- itBundled("default/ErrorMessageCrashStdinIssue2913", {
+ itBundled("default/ErrorMessageCrashStdinESBuildIssue2913", {
// GENERATED
files: {
"/project/node_modules/fflate/package.json": `{ "main": "main.js" }`,
diff --git a/test/bundler/esbuild/importstar.test.ts b/test/bundler/esbuild/importstar.test.ts
index 482655236..c9b78f7be 100644
--- a/test/bundler/esbuild/importstar.test.ts
+++ b/test/bundler/esbuild/importstar.test.ts
@@ -1028,7 +1028,7 @@ describe("bundler", () => {
stdout: '{"foo":"foo"}',
},
});
- itBundled("importstar/Issue176", {
+ itBundled("importstar/ESBuildIssue176", {
files: {
"/entry.js": /* js */ `
import * as things from './folders'
@@ -1102,7 +1102,7 @@ describe("bundler", () => {
stdout: '{"bar":"bar","foo":"foo"}',
},
});
- itBundled("importstar/ImportDefaultNamespaceComboIssue446", {
+ itBundled("importstar/ImportDefaultNamespaceComboESBuildIssue446", {
files: {
"/external-default2.js": /* js */ `
import def, {default as default2} from 'external'
diff --git a/test/bundler/esbuild/importstar_ts.test.ts b/test/bundler/esbuild/importstar_ts.test.ts
index 5641753c0..76afe6d7e 100644
--- a/test/bundler/esbuild/importstar_ts.test.ts
+++ b/test/bundler/esbuild/importstar_ts.test.ts
@@ -1,5 +1,5 @@
import { test, describe } from "bun:test";
-import { expectBundled, itBundled } from "../expectBundled";
+import { RUN_UNCHECKED_TESTS, expectBundled, itBundled } from "../expectBundled";
// Tests ported from:
// https://github.com/evanw/esbuild/blob/main/internal/bundler_tests/bundler_importstar_ts_test.go
@@ -7,7 +7,7 @@ import { expectBundled, itBundled } from "../expectBundled";
// For debug, all files are written to $TEMP/bun-bundle-tests/ts
describe("bundler", () => {
- return;
+ if (!RUN_UNCHECKED_TESTS) return;
itBundled("ts/TSImportStarUnused", {
// GENERATED
files: {
diff --git a/test/bundler/esbuild/loader.test.ts b/test/bundler/esbuild/loader.test.ts
index 648464d6e..1ca741749 100644
--- a/test/bundler/esbuild/loader.test.ts
+++ b/test/bundler/esbuild/loader.test.ts
@@ -1,4 +1,4 @@
-import { expectBundled, itBundled, testForFile } from "../expectBundled";
+import { RUN_UNCHECKED_TESTS, expectBundled, itBundled, testForFile } from "../expectBundled";
var { describe, test, expect } = testForFile(import.meta.path);
// Tests ported from:
@@ -31,7 +31,7 @@ describe("bundler", () => {
},
});
- itBundled("loader/LoaderJSONSharedWithMultipleEntriesIssue413", {
+ itBundled("loader/LoaderJSONSharedWithMultipleEntriesESBuildIssue413", {
// GENERATED
files: {
"/a.js": /* js */ `
@@ -62,8 +62,7 @@ describe("bundler", () => {
},
],
});
-
- return;
+ if (!RUN_UNCHECKED_TESTS) return;
itBundled("loader/LoaderFile", {
// GENERATED
files: {
diff --git a/test/bundler/esbuild/lower.test.ts b/test/bundler/esbuild/lower.test.ts
index b22cdbd74..23b18397d 100644
--- a/test/bundler/esbuild/lower.test.ts
+++ b/test/bundler/esbuild/lower.test.ts
@@ -1,4 +1,4 @@
-import { expectBundled, itBundled, testForFile } from "../expectBundled";
+import { RUN_UNCHECKED_TESTS, expectBundled, itBundled, testForFile } from "../expectBundled";
var { describe, test, expect } = testForFile(import.meta.path);
// Tests ported from:
@@ -7,7 +7,7 @@ var { describe, test, expect } = testForFile(import.meta.path);
// For debug, all files are written to $TEMP/bun-bundle-tests/lower
describe("bundler", () => {
- return;
+ if (!RUN_UNCHECKED_TESTS) return;
itBundled("lower/LowerOptionalCatchNameCollisionNoBundle", {
// GENERATED
files: {
@@ -1207,7 +1207,7 @@ describe("bundler", () => {
},
unsupportedJSFeatures: "es2021",
});
- itBundled("lower/LowerPrivateSuperStaticBundleIssue2158", {
+ itBundled("lower/LowerPrivateSuperStaticBundleESBuildIssue2158", {
// GENERATED
files: {
"/entry.js": /* js */ `
@@ -1451,7 +1451,7 @@ describe("bundler", () => {
unsupportedJSFeatures: "es2018",
mode: "transform",
});
- itBundled("lower/ClassSuperThisIssue242NoBundle", {
+ itBundled("lower/ClassSuperThisESBuildIssue242NoBundle", {
// GENERATED
files: {
"/entry.ts": /* ts */ `
@@ -1678,7 +1678,7 @@ describe("bundler", () => {
},
mode: "passthrough",
});
- itBundled("lower/LowerPrivateClassFieldStaticIssue1424", {
+ itBundled("lower/LowerPrivateClassFieldStaticESBuildIssue1424", {
// GENERATED
files: {
"/entry.js": /* js */ `
@@ -1692,7 +1692,7 @@ describe("bundler", () => {
`,
},
});
- itBundled("lower/LowerNullishCoalescingAssignmentIssue1493", {
+ itBundled("lower/LowerNullishCoalescingAssignmentESBuildIssue1493", {
// GENERATED
files: {
"/entry.js": /* js */ `
diff --git a/test/bundler/esbuild/packagejson.test.ts b/test/bundler/esbuild/packagejson.test.ts
index 0cdb46569..e3e5387b3 100644
--- a/test/bundler/esbuild/packagejson.test.ts
+++ b/test/bundler/esbuild/packagejson.test.ts
@@ -1,4 +1,4 @@
-import { expectBundled, itBundled, testForFile } from "../expectBundled";
+import { RUN_UNCHECKED_TESTS, expectBundled, itBundled, testForFile } from "../expectBundled";
var { describe, test, expect } = testForFile(import.meta.path);
// Tests ported from:
@@ -7,7 +7,7 @@ var { describe, test, expect } = testForFile(import.meta.path);
// For debug, all files are written to $TEMP/bun-bundle-tests/packagejson
describe("bundler", () => {
- return;
+ if (!RUN_UNCHECKED_TESTS) return;
itBundled("packagejson/PackageJsonMain", {
// GENERATED
files: {
@@ -605,7 +605,7 @@ describe("bundler", () => {
"/Users/user/project/src/demo-pkg/ext-browser/index.js": `export let browser = 'browser'`,
},
});
- itBundled("packagejson/PackageJsonBrowserIssue2002A", {
+ itBundled("packagejson/PackageJsonBrowserESBuildIssue2002A", {
// GENERATED
files: {
"/Users/user/project/src/entry.js": `require('pkg/sub')`,
@@ -621,7 +621,7 @@ describe("bundler", () => {
"/Users/user/project/src/node_modules/sub/bar.js": `works()`,
},
});
- itBundled("packagejson/PackageJsonBrowserIssue2002B", {
+ itBundled("packagejson/PackageJsonBrowserESBuildIssue2002B", {
// GENERATED
files: {
"/Users/user/project/src/entry.js": `require('pkg/sub')`,
@@ -637,7 +637,7 @@ describe("bundler", () => {
"/Users/user/project/src/node_modules/pkg/sub/bar.js": `works()`,
},
});
- itBundled("packagejson/PackageJsonBrowserIssue2002C", {
+ itBundled("packagejson/PackageJsonBrowserESBuildIssue2002C", {
// GENERATED
files: {
"/Users/user/project/src/entry.js": `require('pkg/sub')`,
@@ -1857,7 +1857,7 @@ describe("bundler", () => {
NOTE: Node's package format requires that CommonJS files in a "type": "module" package use the ".cjs" file extension.
`, */
});
- itBundled("packagejson/PackageJsonNodePathsIssue2752", {
+ itBundled("packagejson/PackageJsonNodePathsESBuildIssue2752", {
// GENERATED
files: {
"/src/entry.js": /* js */ `
diff --git a/test/bundler/esbuild/splitting.test.ts b/test/bundler/esbuild/splitting.test.ts
index 72d0d70de..1dc2c3d11 100644
--- a/test/bundler/esbuild/splitting.test.ts
+++ b/test/bundler/esbuild/splitting.test.ts
@@ -1,4 +1,4 @@
-import { itBundled, testForFile } from "../expectBundled";
+import { RUN_UNCHECKED_TESTS, itBundled, testForFile } from "../expectBundled";
var { describe, test, expect } = testForFile(import.meta.path);
// Tests ported from:
@@ -31,7 +31,7 @@ describe("bundler", () => {
"/out/b.js": "123",
},
});
- return;
+ if (!RUN_UNCHECKED_TESTS) return;
itBundled("splitting/SplittingSharedCommonJSIntoES6", {
// GENERATED
files: {
@@ -152,7 +152,7 @@ describe("bundler", () => {
splitting: true,
format: "esm",
});
- itBundled("splitting/SplittingCircularReferenceIssue251", {
+ itBundled("splitting/SplittingCircularReferenceESBuildIssue251", {
// GENERATED
files: {
"/a.js": /* js */ `
@@ -195,7 +195,7 @@ describe("bundler", () => {
/* TODO FIX expectedCompileLog: `common.js: WARNING: Import "missing" will always be undefined because the file "empty.js" has no exports
`, */
});
- itBundled("splitting/SplittingReExportIssue273", {
+ itBundled("splitting/SplittingReExportESBuildIssue273", {
// GENERATED
files: {
"/a.js": `export const a = 1`,
@@ -205,7 +205,7 @@ describe("bundler", () => {
splitting: true,
format: "esm",
});
- itBundled("splitting/SplittingDynamicImportIssue272", {
+ itBundled("splitting/SplittingDynamicImportESBuildIssue272", {
// GENERATED
files: {
"/a.js": `import('./b')`,
@@ -215,7 +215,7 @@ describe("bundler", () => {
splitting: true,
format: "esm",
});
- itBundled("splitting/SplittingDynamicImportOutsideSourceTreeIssue264", {
+ itBundled("splitting/SplittingDynamicImportOutsideSourceTreeESBuildIssue264", {
// GENERATED
files: {
"/Users/user/project/src/entry1.js": `import('package')`,
@@ -310,7 +310,7 @@ describe("bundler", () => {
minifyWhitespace: true,
format: "esm",
});
- itBundled("splitting/SplittingMinifyIdentifiersCrashIssue437", {
+ itBundled("splitting/SplittingMinifyIdentifiersCrashESBuildIssue437", {
// GENERATED
files: {
"/a.js": /* js */ `
@@ -329,7 +329,7 @@ describe("bundler", () => {
minifyIdentifiers: true,
format: "esm",
});
- itBundled("splitting/SplittingHybridESMAndCJSIssue617", {
+ itBundled("splitting/SplittingHybridESMAndCJSESBuildIssue617", {
// GENERATED
files: {
"/a.js": `export let foo`,
@@ -358,7 +358,7 @@ describe("bundler", () => {
format: "esm",
splitting: true,
});
- itBundled("splitting/EdgeCaseIssue2793WithSplitting", {
+ itBundled("splitting/EdgeCaseESBuildIssue2793WithSplitting", {
// GENERATED
files: {
"/src/a.js": `export const A = 42;`,
@@ -373,7 +373,7 @@ describe("bundler", () => {
format: "esm",
splitting: true,
});
- itBundled("splitting/EdgeCaseIssue2793WithoutSplitting", {
+ itBundled("splitting/EdgeCaseESBuildIssue2793WithoutSplitting", {
// GENERATED
files: {
"/src/a.js": `export const A = 42;`,
diff --git a/test/bundler/esbuild/ts.test.ts b/test/bundler/esbuild/ts.test.ts
index 5678edc1e..a3ac313a4 100644
--- a/test/bundler/esbuild/ts.test.ts
+++ b/test/bundler/esbuild/ts.test.ts
@@ -1,5 +1,5 @@
import assert from "assert";
-import { itBundled, testForFile } from "../expectBundled";
+import { RUN_UNCHECKED_TESTS, itBundled, testForFile } from "../expectBundled";
var { describe, test, expect } = testForFile(import.meta.path);
// Tests ported from:
@@ -393,18 +393,12 @@ describe("bundler", () => {
const b = api.readFile("/out/b.js");
// make sure the minification trick "enum[enum.K=V]=K" is used, but `enum`
- assert(a.match(/\b[a-zA-Z$]\[[a-zA-Z$]\.A=0]=["']A["']\b/), "should be using enum minification trick (1)");
- assert(a.match(/\b[a-zA-Z$]\[[a-zA-Z$]\.B=1]=["']B["']\b/), "should be using enum minification trick (2)");
- assert(
- a.match(/\b[a-zA-Z$]\[[a-zA-Z$]\.C=[a-zA-Z$]]=["']C["']\b/),
- "should be using enum minification trick (3)",
- );
- assert(b.match(/\b[a-zA-Z$]\[[a-zA-Z$]\.X=0]=["']X["']\b/), "should be using enum minification trick (4)");
- assert(b.match(/\b[a-zA-Z$]\[[a-zA-Z$]\.Y=1]=["']Y["']\b/), "should be using enum minification trick (5)");
- assert(
- b.match(/\b[a-zA-Z$]\[[a-zA-Z$]\.Z=[a-zA-Z$]]=["']Z["']\b/),
- "should be using enum minification trick (6)",
- );
+ assert(a.match(/\b[a-zA-Z$]\[[a-zA-Z$]\.A=0]=["']A["']/), "should be using enum minification trick (1)");
+ assert(a.match(/\b[a-zA-Z$]\[[a-zA-Z$]\.B=1]=["']B["']/), "should be using enum minification trick (2)");
+ assert(a.match(/\b[a-zA-Z$]\[[a-zA-Z$]\.C=[a-zA-Z$]]=["']C["']/), "should be using enum minification trick (3)");
+ assert(b.match(/\b[a-zA-Z$]\[[a-zA-Z$]\.X=0]=["']X["']/), "should be using enum minification trick (4)");
+ assert(b.match(/\b[a-zA-Z$]\[[a-zA-Z$]\.Y=1]=["']Y["']/), "should be using enum minification trick (5)");
+ assert(b.match(/\b[a-zA-Z$]\[[a-zA-Z$]\.Z=[a-zA-Z$]]=["']Z["']/), "should be using enum minification trick (6)");
},
runtimeFiles: {
"/test.js": /* js */ `
@@ -426,7 +420,7 @@ describe("bundler", () => {
`,
},
});
- const TSMinifyNestedEnum = itBundled("ts/TSMinifyNestedEnum", {
+ itBundled("ts/TSMinifyNestedEnum", {
files: {
"/a.ts": `function foo(arg) { enum Foo { A, B, C = Foo, D = arg } return Foo }\ncapture(foo)`,
"/b.ts": `export function foo(arg) { enum Foo { X, Y, Z = Foo, W = arg } return Foo }`,
@@ -508,9 +502,7 @@ describe("bundler", () => {
assert(!b.includes("=>"), "b should not use arrow");
},
});
- return;
itBundled("ts/TSMinifyNamespace", {
- // GENERATED
files: {
"/a.ts": /* ts */ `
namespace Foo {
@@ -518,6 +510,7 @@ describe("bundler", () => {
foo(Foo, Bar)
}
}
+ capture(Foo)
`,
"/b.ts": /* ts */ `
export namespace Foo {
@@ -532,9 +525,22 @@ describe("bundler", () => {
minifyWhitespace: true,
minifyIdentifiers: true,
mode: "transform",
+ onAfterBundle(api) {
+ api.writeFile("/out/a.edited.js", api.readFile("/out/a.js").replace(/capture\((.*?)\)/, `export const Foo = $1`));
+ },
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ let called = false;
+ globalThis.foo = (a, b) => called = true;
+ await import('./out/a.edited.js');
+ assert(called, 'foo should be called from a.ts');
+ called = false;
+ await import('./out/b.js');
+ assert(called, 'foo should be called from b.ts');
+ `,
+ },
});
itBundled("ts/TSMinifyNamespaceNoLogicalAssignment", {
- // GENERATED
files: {
"/a.ts": /* ts */ `
namespace Foo {
@@ -558,9 +564,16 @@ describe("bundler", () => {
outdir: "/",
mode: "transform",
unsupportedJSFeatures: ["logical-assignment"],
+ onAfterBundle(api) {
+ const a = api.readFile("/a.js");
+ assert(a.includes("Bar"), "a should not be empty");
+ assert(!a.includes("||="), "a should not use logical assignment");
+ const b = api.readFile("/b.js");
+ assert(b.includes("Bar"), "b should not be empty");
+ assert(!b.includes("||="), "b should not use logical assignment");
+ },
});
itBundled("ts/TSMinifyNamespaceNoArrow", {
- // GENERATED
files: {
"/a.ts": /* ts */ `
namespace Foo {
@@ -583,9 +596,17 @@ describe("bundler", () => {
minifyIdentifiers: true,
outdir: "/",
mode: "transform",
+ unsupportedJSFeatures: ["arrow"],
+ onAfterBundle(api) {
+ const a = api.readFile("/a.js");
+ assert(a.includes("foo"), "a should not be empty");
+ assert(!a.includes("=>"), "a should not use arrow");
+ const b = api.readFile("/b.js");
+ assert(b.includes("foo"), "b should not be empty");
+ assert(!b.includes("=>"), "b should not use arrow");
+ },
});
itBundled("ts/TSMinifyDerivedClass", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
class Foo extends Bar {
@@ -597,44 +618,71 @@ describe("bundler", () => {
bar();
}
}
+
+ export {Foo}
`,
},
minifySyntax: true,
- unsupportedJSFeatures: "es2015",
- mode: "transform",
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ let calledFoo = false;
+ let calledBar = false;
+ globalThis.foo = () => calledFoo = true;
+ globalThis.bar = () => calledBar = true;
+ globalThis.Bar = class Bar {
+ constructor() {
+ console.log('super')
+ this.hello = 3;
+ }
+ };
+ const {Foo} = await import('./entry.js');
+ import assert from 'assert';
+ const instance = new Foo();
+ console.log(instance.foo, instance.bar, instance.hello);
+ assert(calledFoo, 'foo should be called');
+ assert(calledBar, 'bar should be called');
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: "super\n1 2 3",
+ },
});
itBundled("ts/TSImportVsLocalCollisionAllTypes", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
import {a, b, c, d, e} from './other.ts'
let a
const b = 0
var c
- function d() {}
- class e {}
- console.log(a, b, c, d, e)
+ function d() { return 5; }
+ class e { constructor() { this.prop = 2; }}
+ console.log(JSON.stringify([a, b, c, d(), new e]))
`,
"/other.ts": ``,
},
+ run: {
+ stdout: '[null,0,null,5,{"prop":2}]',
+ },
});
itBundled("ts/TSImportVsLocalCollisionMixed", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
import {a, b, c, d, e, real} from './other.ts'
let a
const b = 0
var c
- function d() {}
- class e {}
- console.log(a, b, c, d, e, real)
+ function d() { return 5; }
+ class e { constructor() { this.prop = 2; }}
+ console.log(JSON.stringify([a, b, c, d(), new e, real]))
`,
"/other.ts": `export let real = 123`,
},
+ run: {
+ stdout: '[null,0,null,5,{"prop":2},123]',
+ },
});
itBundled("ts/TSImportEqualsEliminationTest", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
import a = foo.a
@@ -648,44 +696,62 @@ describe("bundler", () => {
export let bar = c
`,
},
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ globalThis.foo = {
+ a: { b: { c: 123 } },
+ get x() {
+ throw new Error('should not be called')
+ }
+ };
+ const {bar} = await import('./out.js');
+ console.log(bar);
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: "123",
+ },
});
itBundled("ts/TSImportEqualsTreeShakingFalse", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
import { foo } from 'pkg'
import used = foo.used
- import unused = foo.unused
+ import unused_keep = foo.unused
export { used }
`,
},
- mode: "passthrough",
+ treeShaking: false,
+ dce: true,
+ mode: "transform",
});
itBundled("ts/TSImportEqualsTreeShakingTrue", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
import { foo } from 'pkg'
import used = foo.used
- import unused = foo.unused
+ import unused_drop = foo.unused
export { used }
`,
},
- mode: "passthrough",
+ dce: true,
+ treeShaking: true,
+ mode: "transform",
});
itBundled("ts/TSImportEqualsBundle", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
import { foo } from 'pkg'
import used = foo.used
- import unused = foo.unused
+ import unused_drop = foo.unused
export { used }
`,
},
+ dce: true,
+ external: ["pkg"],
});
itBundled("ts/TSMinifiedBundleES6", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
import {foo} from './a'
@@ -700,13 +766,15 @@ describe("bundler", () => {
minifySyntax: true,
minifyWhitespace: true,
minifyIdentifiers: true,
+ run: {
+ stdout: "123",
+ },
});
itBundled("ts/TSMinifiedBundleCommonJS", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
const {foo} = require('./a')
- console.log(foo(), require('./j.json'))
+ console.log(JSON.stringify([foo(), require('./j.json')]))
`,
"/a.ts": /* ts */ `
exports.foo = function() {
@@ -718,9 +786,38 @@ describe("bundler", () => {
minifySyntax: true,
minifyWhitespace: true,
minifyIdentifiers: true,
+ run: {
+ stdout: '[123,{"test":true}]',
+ },
+ });
+ // TODO: all situations with decorators are currently not runtime-checked. as of writing bun crashes when hitting them at all.
+ itBundled("ts/TypeScriptDecoratorsSimpleCase", {
+ files: {
+ "/entry.ts": /* ts */ `
+ function decorator(...args) {
+ console.log('decorator called', JSON.stringify(args))
+ }
+
+ @decorator
+ class Foo {
+ @decorator
+ bar() {
+ console.log('bar called')
+ }
+ }
+
+ new Foo().bar()
+ `,
+ },
+ run: {
+ stdout: `
+ decorator called [{},"bar",{"writable":true,"enumerable":false,"configurable":true}]
+ decorator called [null]
+ bar called
+ `,
+ },
});
itBundled("ts/TypeScriptDecorators", {
- // GENERATED
files: {
"/entry.js": /* js */ `
import all from './all'
@@ -859,19 +956,17 @@ describe("bundler", () => {
},
});
itBundled("ts/TypeScriptDecoratorsKeepNames", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
@decoratorMustComeAfterName
class Foo {}
`,
},
+ keepNames: true,
});
- itBundled("ts/TypeScriptDecoratorScopeIssue2147", {
- // GENERATED
+ itBundled("ts/TypeScriptDecoratorScopeESBuildIssue2147", {
files: {
"/entry.ts": /* ts */ `
- let foo = 1
class Foo {
method1(@dec(foo) foo = 2) {}
method2(@dec(() => foo) foo = 3) {}
@@ -880,7 +975,6 @@ describe("bundler", () => {
class Bar {
static x = class {
static y = () => {
- let bar = 1
@dec(bar)
@dec(() => bar)
class Baz {
@@ -895,10 +989,22 @@ describe("bundler", () => {
}
`,
},
- mode: "passthrough",
+ mode: "transform",
+ onAfterBundle(api) {
+ const capturedCalls = api.captureFile("/out.js", "dec");
+ expect(capturedCalls).toEqual([
+ "foo",
+ "() => foo",
+ "bar",
+ "() => bar",
+ "() => bar",
+ "() => bar",
+ "bar",
+ "() => bar",
+ ]);
+ },
});
- itBundled("ts/TSExportDefaultTypeIssue316", {
- // GENERATED
+ itBundled("ts/TSExportDefaultTypeESBuildIssue316", {
files: {
"/entry.ts": /* ts */ `
import dc_def, { bar as dc } from './keep/declare-class'
@@ -1017,9 +1123,23 @@ describe("bundler", () => {
export let bar = 123
`,
},
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ globalThis.foo = 123456;
+ const mod = (await import('./out.js')).default;
+ console.log(JSON.stringify(mod))
+ console.log(JSON.stringify(mod.map(x => typeof x)))
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: `
+ [123456,123,123456,123,null,123,123456,123,123456,123,{"num":0},123,{"num":0},123,123,123,123,123,123,123]
+ ["number","number","number","number","function","number","number","number","number","number","object","number","object","number","number","number","number","number","number","number"]
+ `,
+ },
});
itBundled("ts/TSImplicitExtensions", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
import './pick-js.js'
@@ -1030,21 +1150,23 @@ describe("bundler", () => {
import './order-jsx.jsx'
`,
"/pick-js.js": `console.log("correct")`,
- "/pick-js.ts": `console.log("wrong")`,
- "/pick-ts.jsx": `console.log("wrong")`,
+ "/pick-js.ts": `console.log("FAILED")`,
+ "/pick-ts.jsx": `console.log("FAILED")`,
"/pick-ts.ts": `console.log("correct")`,
"/pick-jsx.jsx": `console.log("correct")`,
- "/pick-jsx.tsx": `console.log("wrong")`,
- "/pick-tsx.js": `console.log("wrong")`,
+ "/pick-jsx.tsx": `console.log("FAILED")`,
+ "/pick-tsx.js": `console.log("FAILED")`,
"/pick-tsx.tsx": `console.log("correct")`,
"/order-js.ts": `console.log("correct")`,
- "/order-js.tsx": `console.log("wrong")`,
+ "/order-js.tsx": `console.log("FAILED")`,
"/order-jsx.ts": `console.log("correct")`,
- "/order-jsx.tsx": `console.log("wrong")`,
+ "/order-jsx.tsx": `console.log("FAILED")`,
+ },
+ run: {
+ stdout: "correct\n".repeat(6),
},
});
itBundled("ts/TSImplicitExtensionsMissing", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
import './mjs.mjs'
@@ -1059,21 +1181,23 @@ describe("bundler", () => {
"/js.ts.js": ``,
"/jsx.tsx.jsx": ``,
},
- /* TODO FIX expectedScanLog: `entry.ts: ERROR: Could not resolve "./mjs.mjs"
- entry.ts: ERROR: Could not resolve "./cjs.cjs"
- entry.ts: ERROR: Could not resolve "./js.js"
- entry.ts: ERROR: Could not resolve "./jsx.jsx"
- `, */
+ bundleErrors: {
+ "/entry.ts": [
+ `Could not resolve: "./mjs.mjs"`,
+ `Could not resolve: "./cjs.cjs"`,
+ `Could not resolve: "./js.js"`,
+ `Could not resolve: "./jsx.jsx"`,
+ ],
+ },
});
- itBundled("ts/ExportTypeIssue379", {
- // GENERATED
+ itBundled("ts/ExportTypeESBuildIssue379", {
files: {
"/entry.ts": /* ts */ `
import * as A from './a'
import * as B from './b'
import * as C from './c'
import * as D from './d'
- console.log(A, B, C, D)
+ console.log(JSON.stringify([A, B, C, D]))
`,
"/a.ts": /* ts */ `
type Test = Element
@@ -1098,135 +1222,284 @@ describe("bundler", () => {
`,
"/test.ts": `export type Test = Element`,
},
+ run: {
+ stdout: '[{"foo":123},{"foo":123},{"foo":123},{"foo":123}]',
+ },
+ useDefineForClassFields: false,
});
itBundled("ts/ThisInsideFunctionTS", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
- function foo(x = this) { console.log(this) }
+ function foo(x = this) { return [x, this]; }
const objFoo = {
- foo(x = this) { console.log(this) }
+ foo(x = this) { return [x, this]; }
}
class Foo {
x = this
- static y = this.z
- foo(x = this) { console.log(this) }
- static bar(x = this) { console.log(this) }
+ static z = 456;
+ static y = this.z;
+ foo(x = this) { return [x, this]; }
+ static bar(x = this) { return [x, this]; }
}
- new Foo(foo(objFoo))
+
+ assert.deepEqual(foo('bun'), ['bun', undefined]);
+ assert.deepEqual(foo.call('this'), ['this', 'this']);
+ assert.deepEqual(foo.call('this', 'bun'), ['bun', 'this']);
+ assert.deepEqual(objFoo.foo('bun'), ['bun', objFoo]);
+ assert.deepEqual(objFoo.foo(), [objFoo, objFoo]);
+ const fooInstance = new Foo();
+ assert(fooInstance.x === fooInstance, 'Foo#x');
+ assert(Foo.y === 456, 'Foo.y');
+ assert.deepEqual(Foo.bar('bun'), ['bun', Foo]);
+ assert.deepEqual(Foo.bar(), [Foo, Foo]);
+ assert.deepEqual(fooInstance.foo(), [fooInstance, fooInstance]);
+ assert.deepEqual(fooInstance.foo('bun'), ['bun', fooInstance]);
+
if (nested) {
- function bar(x = this) { console.log(this) }
+ function bar(x = this) { return [x, this]; }
const objBar = {
- foo(x = this) { console.log(this) }
+ foo(x = this) { return [x, this]; }
}
class Bar {
x = this
+ static z = 456;
static y = this.z
- foo(x = this) { console.log(this) }
- static bar(x = this) { console.log(this) }
+ foo(x = this) { return [x, this]; }
+ static bar(x = this) { return [x, this]; }
}
- new Bar(bar(objBar))
+
+ assert.deepEqual(bar('bun'), ['bun', undefined]);
+ assert.deepEqual(bar.call('this'), ['this', 'this']);
+ assert.deepEqual(bar.call('this', 'bun'), ['bun', 'this']);
+ assert.deepEqual(objBar.foo('bun'), ['bun', objBar]);
+ assert.deepEqual(objBar.foo(), [objBar, objBar]);
+ const barInstance = new Bar();
+ assert(barInstance.x === barInstance, 'Bar#x');
+ assert(Bar.y === 456, 'Bar.y');
+ assert.deepEqual(Bar.bar('bun'), ['bun', Bar]);
+ assert.deepEqual(Bar.bar(), [Bar, Bar]);
+ assert.deepEqual(barInstance.foo(), [barInstance, barInstance]);
+ assert.deepEqual(barInstance.foo('bun'), ['bun', barInstance]);
}
`,
},
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ globalThis.nested = true;
+ globalThis.assert = (await import('assert')).default;
+ import('./out')
+ `,
+ },
+ run: {
+ file: "/test.js",
+ },
});
itBundled("ts/ThisInsideFunctionTSUseDefineForClassFields", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
- function foo(x = this) { console.log(this) }
+ function foo(x = this) { return [x, this]; }
const objFoo = {
- foo(x = this) { console.log(this) }
+ foo(x = this) { return [x, this]; }
}
class Foo {
x = this
- static y = this.z
- foo(x = this) { console.log(this) }
- static bar(x = this) { console.log(this) }
+ static z = 456;
+ static y = this.z;
+ foo(x = this) { return [x, this]; }
+ static bar(x = this) { return [x, this]; }
}
- new Foo(foo(objFoo))
+
+ assert.deepEqual(foo('bun'), ['bun', undefined]);
+ assert.deepEqual(foo.call('this'), ['this', 'this']);
+ assert.deepEqual(foo.call('this', 'bun'), ['bun', 'this']);
+ assert.deepEqual(objFoo.foo('bun'), ['bun', objFoo]);
+ assert.deepEqual(objFoo.foo(), [objFoo, objFoo]);
+ const fooInstance = new Foo();
+ assert(fooInstance.x === fooInstance, 'Foo#x');
+ assert(Foo.y === 456, 'Foo.y');
+ assert.deepEqual(Foo.bar('bun'), ['bun', Foo]);
+ assert.deepEqual(Foo.bar(), [Foo, Foo]);
+ assert.deepEqual(fooInstance.foo(), [fooInstance, fooInstance]);
+ assert.deepEqual(fooInstance.foo('bun'), ['bun', fooInstance]);
+
if (nested) {
- function bar(x = this) { console.log(this) }
+ function bar(x = this) { return [x, this]; }
const objBar = {
- foo(x = this) { console.log(this) }
+ foo(x = this) { return [x, this]; }
}
class Bar {
x = this
+ static z = 456;
static y = this.z
- foo(x = this) { console.log(this) }
- static bar(x = this) { console.log(this) }
+ foo(x = this) { return [x, this]; }
+ static bar(x = this) { return [x, this]; }
}
- new Bar(bar(objBar))
+
+ assert.deepEqual(bar('bun'), ['bun', undefined]);
+ assert.deepEqual(bar.call('this'), ['this', 'this']);
+ assert.deepEqual(bar.call('this', 'bun'), ['bun', 'this']);
+ assert.deepEqual(objBar.foo('bun'), ['bun', objBar]);
+ assert.deepEqual(objBar.foo(), [objBar, objBar]);
+ const barInstance = new Bar();
+ assert(barInstance.x === barInstance, 'Bar#x');
+ assert(Bar.y === 456, 'Bar.y');
+ assert.deepEqual(Bar.bar('bun'), ['bun', Bar]);
+ assert.deepEqual(Bar.bar(), [Bar, Bar]);
+ assert.deepEqual(barInstance.foo(), [barInstance, barInstance]);
+ assert.deepEqual(barInstance.foo('bun'), ['bun', barInstance]);
}
`,
},
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ globalThis.nested = true;
+ globalThis.assert = (await import('assert')).default;
+ import('./out')
+ `,
+ },
+ run: {
+ file: "/test.js",
+ },
+ useDefineForClassFields: true,
});
itBundled("ts/ThisInsideFunctionTSNoBundle", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
- function foo(x = this) { console.log(this) }
+ function foo(x = this) { return [x, this]; }
const objFoo = {
- foo(x = this) { console.log(this) }
+ foo(x = this) { return [x, this]; }
}
class Foo {
x = this
- static y = this.z
- foo(x = this) { console.log(this) }
- static bar(x = this) { console.log(this) }
+ static z = 456;
+ static y = this.z;
+ foo(x = this) { return [x, this]; }
+ static bar(x = this) { return [x, this]; }
}
- new Foo(foo(objFoo))
+
+ assert.deepEqual(foo('bun'), ['bun', undefined]);
+ assert.deepEqual(foo.call('this'), ['this', 'this']);
+ assert.deepEqual(foo.call('this', 'bun'), ['bun', 'this']);
+ assert.deepEqual(objFoo.foo('bun'), ['bun', objFoo]);
+ assert.deepEqual(objFoo.foo(), [objFoo, objFoo]);
+ const fooInstance = new Foo();
+ assert(fooInstance.x === fooInstance, 'Foo#x');
+ assert(Foo.y === 456, 'Foo.y');
+ assert.deepEqual(Foo.bar('bun'), ['bun', Foo]);
+ assert.deepEqual(Foo.bar(), [Foo, Foo]);
+ assert.deepEqual(fooInstance.foo(), [fooInstance, fooInstance]);
+ assert.deepEqual(fooInstance.foo('bun'), ['bun', fooInstance]);
+
if (nested) {
- function bar(x = this) { console.log(this) }
+ function bar(x = this) { return [x, this]; }
const objBar = {
- foo(x = this) { console.log(this) }
+ foo(x = this) { return [x, this]; }
}
class Bar {
x = this
+ static z = 456;
static y = this.z
- foo(x = this) { console.log(this) }
- static bar(x = this) { console.log(this) }
+ foo(x = this) { return [x, this]; }
+ static bar(x = this) { return [x, this]; }
}
- new Bar(bar(objBar))
+
+ assert.deepEqual(bar('bun'), ['bun', undefined]);
+ assert.deepEqual(bar.call('this'), ['this', 'this']);
+ assert.deepEqual(bar.call('this', 'bun'), ['bun', 'this']);
+ assert.deepEqual(objBar.foo('bun'), ['bun', objBar]);
+ assert.deepEqual(objBar.foo(), [objBar, objBar]);
+ const barInstance = new Bar();
+ assert(barInstance.x === barInstance, 'Bar#x');
+ assert(Bar.y === 456, 'Bar.y');
+ assert.deepEqual(Bar.bar('bun'), ['bun', Bar]);
+ assert.deepEqual(Bar.bar(), [Bar, Bar]);
+ assert.deepEqual(barInstance.foo(), [barInstance, barInstance]);
+ assert.deepEqual(barInstance.foo('bun'), ['bun', barInstance]);
}
`,
},
- mode: "passthrough",
+ mode: "transform",
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ globalThis.nested = true;
+ globalThis.assert = (await import('assert')).default;
+ import('./out')
+ `,
+ },
+ run: {
+ file: "/test.js",
+ },
});
itBundled("ts/ThisInsideFunctionTSNoBundleUseDefineForClassFields", {
// GENERATED
files: {
"/entry.ts": /* ts */ `
- function foo(x = this) { console.log(this) }
+ function foo(x = this) { return [x, this]; }
const objFoo = {
- foo(x = this) { console.log(this) }
+ foo(x = this) { return [x, this]; }
}
class Foo {
x = this
- static y = this.z
- foo(x = this) { console.log(this) }
- static bar(x = this) { console.log(this) }
+ static z = 456;
+ static y = this.z;
+ foo(x = this) { return [x, this]; }
+ static bar(x = this) { return [x, this]; }
}
- new Foo(foo(objFoo))
+
+ assert.deepEqual(foo('bun'), ['bun', undefined]);
+ assert.deepEqual(foo.call('this'), ['this', 'this']);
+ assert.deepEqual(foo.call('this', 'bun'), ['bun', 'this']);
+ assert.deepEqual(objFoo.foo('bun'), ['bun', objFoo]);
+ assert.deepEqual(objFoo.foo(), [objFoo, objFoo]);
+ const fooInstance = new Foo();
+ assert(fooInstance.x === fooInstance, 'Foo#x');
+ assert(Foo.y === 456, 'Foo.y');
+ assert.deepEqual(Foo.bar('bun'), ['bun', Foo]);
+ assert.deepEqual(Foo.bar(), [Foo, Foo]);
+ assert.deepEqual(fooInstance.foo(), [fooInstance, fooInstance]);
+ assert.deepEqual(fooInstance.foo('bun'), ['bun', fooInstance]);
+
if (nested) {
- function bar(x = this) { console.log(this) }
+ function bar(x = this) { return [x, this]; }
const objBar = {
- foo(x = this) { console.log(this) }
+ foo(x = this) { return [x, this]; }
}
class Bar {
x = this
+ static z = 456;
static y = this.z
- foo(x = this) { console.log(this) }
- static bar(x = this) { console.log(this) }
+ foo(x = this) { return [x, this]; }
+ static bar(x = this) { return [x, this]; }
}
- new Bar(bar(objBar))
+
+ assert.deepEqual(bar('bun'), ['bun', undefined]);
+ assert.deepEqual(bar.call('this'), ['this', 'this']);
+ assert.deepEqual(bar.call('this', 'bun'), ['bun', 'this']);
+ assert.deepEqual(objBar.foo('bun'), ['bun', objBar]);
+ assert.deepEqual(objBar.foo(), [objBar, objBar]);
+ const barInstance = new Bar();
+ assert(barInstance.x === barInstance, 'Bar#x');
+ assert(Bar.y === 456, 'Bar.y');
+ assert.deepEqual(Bar.bar('bun'), ['bun', Bar]);
+ assert.deepEqual(Bar.bar(), [Bar, Bar]);
+ assert.deepEqual(barInstance.foo(), [barInstance, barInstance]);
+ assert.deepEqual(barInstance.foo('bun'), ['bun', barInstance]);
}
`,
},
- mode: "passthrough",
+ mode: "transform",
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ globalThis.nested = true;
+ globalThis.assert = (await import('assert')).default;
+ import('./out')
+ `,
+ },
+ run: {
+ file: "/test.js",
+ },
});
itBundled("ts/TSComputedClassFieldUseDefineFalse", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
class Foo {
@@ -1237,13 +1510,43 @@ describe("bundler", () => {
@dec
[y] = z;
}
- new Foo()
+ export default Foo;
`,
},
- mode: "passthrough",
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ globalThis.q = 'q1';
+ globalThis.r = 'r1';
+ globalThis.s = 's1';
+ globalThis.x = 'x1';
+ globalThis.y = 'y1';
+ globalThis.z = 'z1';
+ globalThis.dec = function(...args) {
+ console.log(JSON.stringify([this, ...args]));
+ };
+ const Foo = (await import('./out')).default;
+ globalThis.q = 'q2';
+ globalThis.r = 'r2';
+ globalThis.s = 's2';
+ globalThis.x = 'x2';
+ globalThis.y = 'y2';
+ globalThis.z = 'z2';
+ const y = new Foo();
+ console.log(JSON.stringify(y));
+ `,
+ },
+ useDefineForClassFields: false,
+ mode: "transform",
+ run: {
+ stdout: `
+ [null,{},"x1",null]
+ [null,{},"y1",null]
+ {"r1":"s2","y1":"z2"}
+ `,
+ file: "/test.js",
+ },
});
itBundled("ts/TSComputedClassFieldUseDefineTrue", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
class Foo {
@@ -1254,13 +1557,43 @@ describe("bundler", () => {
@dec
[y] = z;
}
- new Foo()
+ export default Foo;
`,
},
- mode: "passthrough",
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ globalThis.q = 'q1';
+ globalThis.r = 'r1';
+ globalThis.s = 's1';
+ globalThis.x = 'x1';
+ globalThis.y = 'y1';
+ globalThis.z = 'z1';
+ globalThis.dec = function(...args) {
+ console.log(JSON.stringify([this, ...args]));
+ };
+ const Foo = (await import('./out')).default;
+ globalThis.q = 'q2';
+ globalThis.r = 'r2';
+ globalThis.s = 's2';
+ globalThis.x = 'x2';
+ globalThis.y = 'y2';
+ globalThis.z = 'z2';
+ const y = new Foo();
+ console.log(JSON.stringify(y));
+ `,
+ },
+ useDefineForClassFields: true,
+ mode: "transform",
+ run: {
+ stdout: `
+ [null,{},"x1",null]
+ [null,{},"y1",null]
+ {"r1":"s2","y1":"z2"}
+ `,
+ file: "/test.js",
+ },
});
itBundled("ts/TSComputedClassFieldUseDefineTrueLower", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
class Foo {
@@ -1271,14 +1604,44 @@ describe("bundler", () => {
@dec
[y] = z;
}
- new Foo()
+ export default Foo;
+ `,
+ },
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ globalThis.q = 'q1';
+ globalThis.r = 'r1';
+ globalThis.s = 's1';
+ globalThis.x = 'x1';
+ globalThis.y = 'y1';
+ globalThis.z = 'z1';
+ globalThis.dec = function(...args) {
+ console.log(JSON.stringify([this, ...args]));
+ };
+ const Foo = (await import('./out')).default;
+ globalThis.q = 'q2';
+ globalThis.r = 'r2';
+ globalThis.s = 's2';
+ globalThis.x = 'x2';
+ globalThis.y = 'y2';
+ globalThis.z = 'z2';
+ const y = new Foo();
+ console.log(JSON.stringify(y));
`,
},
useDefineForClassFields: true,
- mode: "passthrough",
+ mode: "transform",
+ run: {
+ stdout: `
+ [null,{},"x1",null]
+ [null,{},"y1",null]
+ {"r1":"s2","y1":"z2"}
+ `,
+ file: "/test.js",
+ },
+ unsupportedJSFeatures: ["class-field"],
});
itBundled("ts/TSAbstractClassFieldUseAssign", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
const keepThis = Symbol('keepThis')
@@ -1288,15 +1651,15 @@ describe("bundler", () => {
[keepThis]: any
abstract REMOVE_THIS_TOO: any
abstract [AND_REMOVE_THIS]: any
- abstract [(x => y => x + y)('nested')('scopes')]: any
+ abstract [(x => y => x + y)('nested')('scopes_REMOVE')]: any
}
(() => new Foo())()
`,
},
- mode: "passthrough",
+ dce: true,
+ useDefineForClassFields: false,
});
itBundled("ts/TSAbstractClassFieldUseDefine", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
const keepThisToo = Symbol('keepThisToo')
@@ -1311,24 +1674,28 @@ describe("bundler", () => {
(() => new Foo())()
`,
},
- mode: "passthrough",
+ mode: "transform",
+ useDefineForClassFields: true,
});
itBundled("ts/TSImportMTS", {
- // GENERATED
files: {
"/entry.ts": `import './imported.mjs'`,
"/imported.mts": `console.log('works')`,
},
+ run: {
+ stdout: "works",
+ },
});
itBundled("ts/TSImportCTS", {
- // GENERATED
files: {
"/entry.ts": `require('./required.cjs')`,
"/required.cjs": `console.log('works')`,
},
+ run: {
+ stdout: "works",
+ },
});
itBundled("ts/TSSideEffectsFalseWarningTypeDeclarations", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
import "some-js"
@@ -1348,14 +1715,11 @@ describe("bundler", () => {
"/node_modules/empty-dts/package.json": `{ "main": "./foo.d.ts", "sideEffects": false }`,
"/node_modules/empty-dts/foo.d.ts": `export type Foo = number`,
},
- /* TODO FIX expectedScanLog: `entry.ts: WARNING: Ignoring this import because "node_modules/some-js/foo.js" was marked as having no side effects
- node_modules/some-js/package.json: NOTE: "sideEffects" is false in the enclosing "package.json" file:
- entry.ts: WARNING: Ignoring this import because "node_modules/some-ts/foo.ts" was marked as having no side effects
- node_modules/some-ts/package.json: NOTE: "sideEffects" is false in the enclosing "package.json" file:
- `, */
+ onAfterBundle(api) {
+ expect(api.readFile("/out.js").trim()).toBe("");
+ },
});
itBundled("ts/TSSiblingNamespace", {
- // GENERATED
files: {
"/let.ts": /* ts */ `
export namespace x { export let y = 123 }
@@ -1379,8 +1743,24 @@ describe("bundler", () => {
`,
},
entryPoints: ["/let.ts", "/function.ts", "/class.ts", "/namespace.ts", "/enum.ts"],
- mode: "passthrough",
+ mode: "transform",
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ import assert from 'assert'
+ const test_let = (await import('./let.js')).x
+ assert(test_let.x === test_let.x, "let.ts worked")
+ const test_function = (await import('./function.js')).x
+ assert(test_function.x === test_function.x, "function.ts worked")
+ const test_class = (await import('./class.js')).x
+ assert(test_class.x === test_class.x, "class.ts worked")
+ const test_namespace = (await import('./namespace.js')).x
+ assert(test_namespace.x === test_namespace.x, "namespace.ts worked")
+ const test_enum = (await import('./enum.js')).x
+ assert(test_enum.x === test_enum.x, "enum.ts worked")
+ `,
+ },
});
+ if (!RUN_UNCHECKED_TESTS) return;
itBundled("ts/TSSiblingEnum", {
// GENERATED
files: {
diff --git a/test/bundler/expectBundled.md b/test/bundler/expectBundled.md
index 4e944543c..be455910c 100644
--- a/test/bundler/expectBundled.md
+++ b/test/bundler/expectBundled.md
@@ -188,3 +188,23 @@ Places that are not required to be dce'd contain `POSSIBLE_REMOVAL` and do not t
## keepNames tricks
In `esbuild/default.test.ts`, test `default/KeepNamesTreeShaking`, we call the esbuild cli to minify identifiers, and then check the code for expected class names to survive the minification (keep names forcibily sets functions `.name`).
+
+# capture
+
+This lets you capture the exact js that is emitted by wrapping it in a function call `capture`. Like a partial snapshot.
+
+```ts
+itBundled("minify/TemplateStringFolding", {
+ files: {
+ "/entry.js": /* js */ `
+ capture(\`😋📋👌\`.length)
+ capture(\`😋📋👌\`.length === 6)
+ capture(\`😋📋👌\`.length == 6)
+ capture(\`😋📋👌\`.length === 2)
+ capture(\`😋📋👌\`.length == 2)
+ `,
+ },
+ minifySyntax: true,
+ capture: ["6", "true", "true", "false", "false"],
+});
+```
diff --git a/test/bundler/expectBundled.ts b/test/bundler/expectBundled.ts
index 0b467dddf..3bffb4271 100644
--- a/test/bundler/expectBundled.ts
+++ b/test/bundler/expectBundled.ts
@@ -32,6 +32,16 @@ const DEBUG = process.env.BUN_BUNDLER_TEST_DEBUG;
const FILTER = process.env.BUN_BUNDLER_TEST_FILTER;
/** Path to the bun. TODO: Once bundler is merged, we should remove the `bun-debug` fallback. */
const BUN_EXE = (process.env.BUN_EXE && Bun.which(process.env.BUN_EXE)) ?? Bun.which("bun-debug") ?? bunExe();
+/**
+ * If set to true, run an alternate validation for tests which is much looser.
+ * We are only testing for:
+ * - bundler does not crash
+ * - output js has no syntax errors
+ *
+ * Defaults to true unless you are running a single test.
+ */
+const LOOSE = !process.env.BUN_BUNDLER_TEST_FILTER && process.env.BUN_BUNDLER_TEST_LOOSE !== "false";
+export const RUN_UNCHECKED_TESTS = LOOSE;
const outBaseTemplate = path.join(tmpdir(), "bun-build-tests", `${ESBUILD ? "esbuild" : "bun"}-`);
if (!existsSync(path.dirname(outBaseTemplate))) mkdirSync(path.dirname(outBaseTemplate), { recursive: true });
@@ -57,7 +67,7 @@ export interface BundlerTestInput {
entryPointsRaw?: string[];
/** Defaults to bundle */
mode?: "bundle" | "transform";
- /** Used for `default/ErrorMessageCrashStdinIssue2913`. */
+ /** Used for `default/ErrorMessageCrashStdinESBuildIssue2913`. */
stdin?: { contents: string; cwd: string };
/** Use when doing something weird with entryPoints and you need to check other output paths. */
outputPaths?: string[];
@@ -106,6 +116,7 @@ export interface BundlerTestInput {
treeShaking?: boolean;
unsupportedCSSFeatures?: string[];
unsupportedJSFeatures?: string[];
+ /** if set to true or false, create or edit tsconfig.json to set compilerOptions.useDefineForClassFields */
useDefineForClassFields?: boolean;
sourceMap?: boolean | "inline" | "external";
@@ -152,6 +163,9 @@ export interface BundlerTestInput {
files: string[];
};
+ /** Captures `capture()` function calls in the output. */
+ capture?: string[];
+
/** Run after bundle happens but before runtime. */
onAfterBundle?(api: BundlerTestBundleAPI): void;
}
@@ -167,6 +181,10 @@ export interface BundlerTestBundleAPI {
appendFile(file: string, contents: string): void;
expectFile(file: string): Expect;
assertFileExists(file: string): void;
+ /**
+ * Finds all `capture(...)` calls and returns the strings within each function call.
+ */
+ captureFile(file: string, fnName?: string): string[];
warnings: Record<string, { file: string; error: string; line?: string; col?: string }[]>;
options: BundlerTestInput;
@@ -220,6 +238,7 @@ export function expectBundled(
banner,
bundleErrors,
bundleWarnings,
+ capture,
dce,
dceKeepMarkerCount,
define,
@@ -236,6 +255,7 @@ export function expectBundled(
legalComments,
loader,
mainFields,
+ matchesReference,
metafile,
minifyIdentifiers,
minifySyntax,
@@ -255,7 +275,7 @@ export function expectBundled(
treeShaking,
unsupportedCSSFeatures,
unsupportedJSFeatures,
- matchesReference,
+ useDefineForClassFields,
...unknownProps
} = opts;
@@ -287,6 +307,12 @@ export function expectBundled(
if (!ESBUILD && format !== "esm") {
throw new Error("formats besides esm not implemented in bun build");
}
+ if (!ESBUILD && platform === "neutral") {
+ throw new Error("platform=neutral not implemented in bun build");
+ }
+ if (!ESBUILD && mode === "transform") {
+ throw new Error("mode=transform not implemented in bun build");
+ }
if (!ESBUILD && metafile) {
throw new Error("metafile not implemented in bun build");
}
@@ -358,6 +384,26 @@ export function expectBundled(
writeFileSync(filename, dedent(contents).replace(/\{\{root\}\}/g, root));
}
+ if (useDefineForClassFields !== undefined) {
+ if (existsSync(path.join(root, "tsconfig.json"))) {
+ try {
+ const tsconfig = JSON.parse(readFileSync(path.join(root, "tsconfig.json"), "utf8"));
+ tsconfig.compilerOptions = tsconfig.compilerOptions ?? {};
+ tsconfig.compilerOptions.useDefineForClassFields = useDefineForClassFields;
+ writeFileSync(path.join(root, "tsconfig.json"), JSON.stringify(tsconfig, null, 2));
+ } catch (error) {
+ console.log(
+ "DEBUG NOTE: specifying useDefineForClassFields causes tsconfig.json to be parsed as JSON and not JSONC.",
+ );
+ }
+ } else {
+ writeFileSync(
+ path.join(root, "tsconfig.json"),
+ JSON.stringify({ compilerOptions: { useDefineForClassFields } }, null, 2),
+ );
+ }
+ }
+
// Run bun build cli. In the future we can move to using `Bun.Bundler`
const cmd = (
!ESBUILD
@@ -391,6 +437,7 @@ export function expectBundled(
// keepNames && `--keep-names`,
// mainFields && `--main-fields=${mainFields}`,
// loader && Object.entries(loader).map(([k, v]) => ["--loader", `${k}=${v}`]),
+ mode === "transform" && "--transform",
]
: [
ESBUILD_PATH,
@@ -478,8 +525,9 @@ export function expectBundled(
if (!success) {
if (!ESBUILD) {
+ const errorText = stderr.toString("utf-8");
const errorRegex = /^error: (.*?)\n(?:.*?\n\s*\^\s*\n(.*?)\n)?/gms;
- const allErrors = [...stderr!.toString("utf-8").matchAll(errorRegex)]
+ const allErrors = [...errorText.matchAll(errorRegex)]
.map(([_str1, error, source]) => {
if (!source) {
if (error === "FileNotFound") {
@@ -494,10 +542,16 @@ export function expectBundled(
.filter(Boolean) as any[];
if (allErrors.length === 0) {
- console.log(stderr!.toString("utf-8"));
+ console.log(errorText);
}
- if (stderr!.toString("utf-8").includes("Crash report saved to:")) {
+ if (
+ errorText.includes("Crash report saved to:") ||
+ errorText.includes("panic: reached unreachable code") ||
+ errorText.includes("Panic: reached unreachable code") ||
+ errorText.includes("Segmentation fault") ||
+ errorText.includes("bun has crashed")
+ ) {
throw new Error("Bun crashed during build");
}
@@ -639,6 +693,15 @@ export function expectBundled(
},
warnings: warningReference,
options: opts,
+ captureFile: (file, fnName = "capture") => {
+ const fileContents = readFile(file);
+ const regex = new RegExp(`\\b${fnName}\\s*\\(((?:\\(\\))?.*?)\\)`, "g");
+ const matches = [...fileContents.matchAll(regex)];
+ if (matches.length === 0) {
+ throw new Error(`No ${fnName} calls found in ${file}`);
+ }
+ return matches.map(match => match[1]);
+ },
} satisfies BundlerTestBundleAPI;
// DCE keep scan
@@ -750,6 +813,11 @@ export function expectBundled(
}
}
+ if (capture) {
+ const captures = api.captureFile(path.relative(root, outfile ?? outputPaths[0]));
+ expect(captures).toEqual(capture);
+ }
+
// Write runtime files to disk as well as run the post bundle hook.
for (const [file, contents] of Object.entries(runtimeFiles ?? {})) {
mkdirSync(path.dirname(path.join(root, file)), { recursive: true });
@@ -907,7 +975,22 @@ export function itBundled(id: string, opts: BundlerTestInput): BundlerTestRef {
}
}
- it(id, () => expectBundled(id, opts));
+ if (LOOSE) {
+ try {
+ expectBundled(id, opts);
+ it(id, () => {});
+ } catch (error: any) {
+ if (error.message === "Bun crashed during build") {
+ it(id, () => {
+ throw error;
+ });
+ } else {
+ it.skip(id, () => {});
+ }
+ }
+ } else {
+ it(id, () => expectBundled(id, opts));
+ }
return ref;
}
itBundled.skip = (id: string, opts: BundlerTestInput) => {
diff --git a/test/bundler/report-bundler-test-progress.sh b/test/bundler/report-bundler-test-progress.sh
index 1ee0fec66..7c0266c59 100644
--- a/test/bundler/report-bundler-test-progress.sh
+++ b/test/bundler/report-bundler-test-progress.sh
@@ -12,7 +12,7 @@ total_skip=0
for test in $tests; do
defined=$(grep "^import" $test -v | grep -v expectBundled.md | grep -Ec "expectBundled|itBundled")
- output=$(bun test $test 2>&1 | tail -n 5)
+ output=$(BUN_BUNDLER_TEST_LOOSE=false bun test $test 2>&1 | tail -n 5)
pass=$(echo "$output" | grep "pass" | cut -d " " -f 2)
fail=$(echo "$output" | grep "fail" | cut -d " " -f 2)
skip=$(echo "$output" | grep "skip" | cut -d " " -f 2)
diff --git a/test/bundler/run-single-bundler-test.sh b/test/bundler/run-single-bundler-test.sh
index 0b3cf96b2..a22aee77d 100755
--- a/test/bundler/run-single-bundler-test.sh
+++ b/test/bundler/run-single-bundler-test.sh
@@ -6,6 +6,10 @@ if [ -z "$1" ]; then
exit 1
fi
+if [ -z "$BUN_EXE"]; then
+ BUN_EXE=$(which bd 2>/dev/null || which bun 2>/dev/null)
+fi
+
__dirname="$(dirname $(realpath "$0"))"
cd "$__dirname"