aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar dave caruso <me@paperdave.net> 2023-04-11 21:18:49 -0400
committerGravatar GitHub <noreply@github.com> 2023-04-11 18:18:49 -0700
commit519f9aac8081d961f8e79062f4c0f26ef256027b (patch)
treee65dbc411e6f745726904e19349dbd2af7ebbd28
parenta744f5369d356424c3039f9dcf6d7f9412565ecf (diff)
downloadbun-519f9aac8081d961f8e79062f4c0f26ef256027b.tar.gz
bun-519f9aac8081d961f8e79062f4c0f26ef256027b.tar.zst
bun-519f9aac8081d961f8e79062f4c0f26ef256027b.zip
finish refining bundler tests (#2623)
* bundler tests * test refining, 257/847 * bundler tests, 298/849 * more bundler tests * finish dce
-rwxr-xr-xtest/bun.lockbbin36614 -> 36614 bytes
-rw-r--r--test/bundler/bundler_cjs2esm.test.ts8
-rw-r--r--test/bundler/bundler_edgecase.test.ts28
-rw-r--r--test/bundler/esbuild/dce.test.ts1235
-rw-r--r--test/bundler/esbuild/default.test.ts930
-rw-r--r--test/bundler/expectBundled.md6
-rw-r--r--test/bundler/expectBundled.ts133
-rw-r--r--test/bundler/report-bundler-test-progress.sh43
-rwxr-xr-xtest/bundler/run-single-bundler-test.sh2
-rw-r--r--test/bundler/transpiler.test.js10
-rw-r--r--test/package.json2
11 files changed, 1568 insertions, 829 deletions
diff --git a/test/bun.lockb b/test/bun.lockb
index f95cf3658..d1c1732e4 100755
--- a/test/bun.lockb
+++ b/test/bun.lockb
Binary files differ
diff --git a/test/bundler/bundler_cjs2esm.test.ts b/test/bundler/bundler_cjs2esm.test.ts
new file mode 100644
index 000000000..4c37e9591
--- /dev/null
+++ b/test/bundler/bundler_cjs2esm.test.ts
@@ -0,0 +1,8 @@
+import assert from "assert";
+import dedent from "dedent";
+import { bundlerTest, expectBundled, itBundled, testForFile } from "./expectBundled";
+var { describe, test, expect } = testForFile(import.meta.path);
+
+describe("bundler", () => {
+ return;
+});
diff --git a/test/bundler/bundler_edgecase.test.ts b/test/bundler/bundler_edgecase.test.ts
index d57fd3281..cb8a90b81 100644
--- a/test/bundler/bundler_edgecase.test.ts
+++ b/test/bundler/bundler_edgecase.test.ts
@@ -23,4 +23,32 @@ describe("bundler", () => {
},
run: { stdout: "foo" },
});
+ itBundled("edgecase/ImportStarSyntaxErrorBug", {
+ // bug: 'import {ns}, * as import_x from "x";'
+ files: {
+ "/entry.js": /* js */ `
+ export {ns} from 'x'
+ export * as ns2 from 'x'
+ `,
+ },
+ external: ["x"],
+ runtimeFiles: {
+ "/node_modules/x/index.js": `export const ns = 1`,
+ },
+ 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.
+
+ // // console.log;
+
+ // fn2("TODO: should this call be kept?");
+ // })();
+ // `,
+ // },
+ // dce: true,
+ // });
});
diff --git a/test/bundler/esbuild/dce.test.ts b/test/bundler/esbuild/dce.test.ts
index 2cd8a971b..8b3e2c289 100644
--- a/test/bundler/esbuild/dce.test.ts
+++ b/test/bundler/esbuild/dce.test.ts
@@ -1,3 +1,5 @@
+import assert from "assert";
+import dedent from "dedent";
import { expectBundled, itBundled, testForFile } from "../expectBundled";
var { describe, test, expect } = testForFile(import.meta.path);
@@ -6,10 +8,10 @@ var { describe, test, expect } = testForFile(import.meta.path);
// For debug, all files are written to $TEMP/bun-bundle-tests/dce
+// To understand what `dce: true` is doing, see ../expectBundled.md's "dce: true" section
+
describe("bundler", () => {
- return;
itBundled("dce/PackageJsonSideEffectsFalseKeepNamedImportES6", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import {foo} from "demo-pkg"
@@ -25,9 +27,11 @@ describe("bundler", () => {
}
`,
},
+ run: {
+ stdout: "hello\n123",
+ },
});
itBundled("dce/PackageJsonSideEffectsFalseKeepNamedImportCommonJS", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import {foo} from "demo-pkg"
@@ -43,13 +47,15 @@ describe("bundler", () => {
}
`,
},
+ run: {
+ stdout: "hello\n123",
+ },
});
itBundled("dce/PackageJsonSideEffectsFalseKeepStarImportES6", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import * as ns from "demo-pkg"
- console.log(ns)
+ console.log(JSON.stringify(ns))
`,
"/Users/user/project/node_modules/demo-pkg/index.js": /* js */ `
export const foo = 123
@@ -61,13 +67,15 @@ describe("bundler", () => {
}
`,
},
+ run: {
+ stdout: 'hello\n{"foo":123}',
+ },
});
itBundled("dce/PackageJsonSideEffectsFalseKeepStarImportCommonJS", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import * as ns from "demo-pkg"
- console.log(ns)
+ console.log(JSON.stringify(ns))
`,
"/Users/user/project/node_modules/demo-pkg/index.js": /* js */ `
exports.foo = 123
@@ -79,16 +87,18 @@ describe("bundler", () => {
}
`,
},
+ run: {
+ stdout: 'hello\n{"default":{"foo":123},"foo":123}',
+ },
});
itBundled("dce/PackageJsonSideEffectsTrueKeepES6", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import "demo-pkg"
console.log('unused import')
`,
"/Users/user/project/node_modules/demo-pkg/index.js": /* js */ `
- export const foo = 123
+ export const foo = "FAILED"
console.log('hello')
`,
"/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
@@ -97,9 +107,12 @@ describe("bundler", () => {
}
`,
},
+ dce: true,
+ run: {
+ stdout: "hello\nunused import",
+ },
});
itBundled("dce/PackageJsonSideEffectsTrueKeepCommonJS", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import "demo-pkg"
@@ -115,9 +128,12 @@ describe("bundler", () => {
}
`,
},
+ dce: true,
+ run: {
+ stdout: "hello\nunused import",
+ },
});
itBundled("dce/PackageJsonSideEffectsFalseKeepBareImportAndRequireES6", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import "demo-pkg"
@@ -134,12 +150,12 @@ describe("bundler", () => {
}
`,
},
- /* TODO FIX expectedScanLog: `Users/user/project/src/entry.js: WARNING: Ignoring this import because "Users/user/project/node_modules/demo-pkg/index.js" was marked as having no side effects
- Users/user/project/node_modules/demo-pkg/package.json: NOTE: "sideEffects" is false in the enclosing "package.json" file:
- `, */
+ dce: true,
+ run: {
+ stdout: "hello\nunused import",
+ },
});
itBundled("dce/PackageJsonSideEffectsFalseKeepBareImportAndRequireCommonJS", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import "demo-pkg"
@@ -156,20 +172,20 @@ describe("bundler", () => {
}
`,
},
- /* TODO FIX expectedScanLog: `Users/user/project/src/entry.js: WARNING: Ignoring this import because "Users/user/project/node_modules/demo-pkg/index.js" was marked as having no side effects
- Users/user/project/node_modules/demo-pkg/package.json: NOTE: "sideEffects" is false in the enclosing "package.json" file:
- `, */
+ dce: true,
+ run: {
+ stdout: "hello\nunused import",
+ },
});
itBundled("dce/PackageJsonSideEffectsFalseRemoveBareImportES6", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import "demo-pkg"
console.log('unused import')
`,
"/Users/user/project/node_modules/demo-pkg/index.js": /* js */ `
- export const foo = 123
- console.log('hello')
+ export const foo = "TEST FAILED"
+ console.log('TEST FAILED')
`,
"/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
{
@@ -177,20 +193,20 @@ describe("bundler", () => {
}
`,
},
- /* TODO FIX expectedScanLog: `Users/user/project/src/entry.js: WARNING: Ignoring this import because "Users/user/project/node_modules/demo-pkg/index.js" was marked as having no side effects
- Users/user/project/node_modules/demo-pkg/package.json: NOTE: "sideEffects" is false in the enclosing "package.json" file:
- `, */
+ dce: true,
+ run: {
+ stdout: "unused import",
+ },
});
itBundled("dce/PackageJsonSideEffectsFalseRemoveBareImportCommonJS", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import "demo-pkg"
console.log('unused import')
`,
"/Users/user/project/node_modules/demo-pkg/index.js": /* js */ `
- exports.foo = 123
- console.log('hello')
+ exports.foo = "TEST FAILED"
+ console.log('TEST FAILED')
`,
"/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
{
@@ -198,20 +214,20 @@ describe("bundler", () => {
}
`,
},
- /* TODO FIX expectedScanLog: `Users/user/project/src/entry.js: WARNING: Ignoring this import because "Users/user/project/node_modules/demo-pkg/index.js" was marked as having no side effects
- Users/user/project/node_modules/demo-pkg/package.json: NOTE: "sideEffects" is false in the enclosing "package.json" file:
- `, */
+ dce: true,
+ run: {
+ stdout: "unused import",
+ },
});
itBundled("dce/PackageJsonSideEffectsFalseRemoveNamedImportES6", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import {foo} from "demo-pkg"
console.log('unused import')
`,
"/Users/user/project/node_modules/demo-pkg/index.js": /* js */ `
- export const foo = 123
- console.log('hello')
+ export const foo = "TEST FAILED"
+ console.log('TEST FAILED')
`,
"/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
{
@@ -219,17 +235,20 @@ describe("bundler", () => {
}
`,
},
+ dce: true,
+ run: {
+ stdout: "unused import",
+ },
});
itBundled("dce/PackageJsonSideEffectsFalseRemoveNamedImportCommonJS", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import {foo} from "demo-pkg"
console.log('unused import')
`,
"/Users/user/project/node_modules/demo-pkg/index.js": /* js */ `
- exports.foo = 123
- console.log('hello')
+ exports.foo = "TEST FAILED"
+ console.log('TEST FAILED')
`,
"/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
{
@@ -237,17 +256,20 @@ describe("bundler", () => {
}
`,
},
+ dce: true,
+ run: {
+ stdout: "unused import",
+ },
});
itBundled("dce/PackageJsonSideEffectsFalseRemoveStarImportES6", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import * as ns from "demo-pkg"
console.log('unused import')
`,
"/Users/user/project/node_modules/demo-pkg/index.js": /* js */ `
- export const foo = 123
- console.log('hello')
+ export const foo = "TEST FAILED"
+ console.log('TEST FAILED')
`,
"/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
{
@@ -255,17 +277,20 @@ describe("bundler", () => {
}
`,
},
+ dce: true,
+ run: {
+ stdout: "unused import",
+ },
});
itBundled("dce/PackageJsonSideEffectsFalseRemoveStarImportCommonJS", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import * as ns from "demo-pkg"
console.log('unused import')
`,
"/Users/user/project/node_modules/demo-pkg/index.js": /* js */ `
- exports.foo = 123
- console.log('hello')
+ exports.foo = "TEST FAILED"
+ console.log('TEST FAILED')
`,
"/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
{
@@ -273,16 +298,19 @@ describe("bundler", () => {
}
`,
},
+ dce: true,
+ run: {
+ stdout: "unused import",
+ },
});
itBundled("dce/PackageJsonSideEffectsArrayRemove", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import {foo} from "demo-pkg"
console.log('unused import')
`,
"/Users/user/project/node_modules/demo-pkg/index.js": /* js */ `
- export const foo = 123
+ export const foo = "TEST FAILED"
console.log('hello')
`,
"/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
@@ -291,17 +319,20 @@ describe("bundler", () => {
}
`,
},
+ dce: true,
+ run: {
+ stdout: "unused import",
+ },
});
itBundled("dce/PackageJsonSideEffectsArrayKeep", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import {foo} from "demo-pkg"
console.log('unused import')
`,
"/Users/user/project/node_modules/demo-pkg/index.js": /* js */ `
- export const foo = 123
- console.log('hello')
+ export const foo = "TEST FAILED"
+ console.log("hello")
`,
"/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
{
@@ -309,20 +340,23 @@ describe("bundler", () => {
}
`,
},
+ dce: true,
+ run: {
+ stdout: "hello\nunused import",
+ },
});
itBundled("dce/PackageJsonSideEffectsArrayKeepMainUseModule", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import {foo} from "demo-pkg"
console.log('unused import')
`,
"/Users/user/project/node_modules/demo-pkg/index-main.js": /* js */ `
- export const foo = 123
+ export const foo = "TEST FAILED"
console.log('TEST FAILED')
`,
"/Users/user/project/node_modules/demo-pkg/index-module.js": /* js */ `
- export const foo = 123
+ export const foo = "TEST FAILED"
console.log('TEST FAILED')
`,
"/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
@@ -333,20 +367,24 @@ describe("bundler", () => {
}
`,
},
+ dce: true,
+ mainFields: ["module"],
+ run: {
+ stdout: "unused import",
+ },
});
itBundled("dce/PackageJsonSideEffectsArrayKeepMainUseMain", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import {foo} from "demo-pkg"
console.log('unused import')
`,
"/Users/user/project/node_modules/demo-pkg/index-main.js": /* js */ `
- export const foo = 123
+ export const foo = "TEST FAILED"
console.log('this should be kept')
`,
"/Users/user/project/node_modules/demo-pkg/index-module.js": /* js */ `
- export const foo = 123
+ export const foo = "TEST FAILED"
console.log('TEST FAILED')
`,
"/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
@@ -357,20 +395,24 @@ describe("bundler", () => {
}
`,
},
+ dce: true,
+ mainFields: ["main"],
+ run: {
+ stdout: "this should be kept\nunused import",
+ },
});
itBundled("dce/PackageJsonSideEffectsArrayKeepMainImplicitModule", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import {foo} from "demo-pkg"
console.log('unused import')
`,
"/Users/user/project/node_modules/demo-pkg/index-main.js": /* js */ `
- export const foo = 123
+ export const foo = "TEST FAILED"
console.log('TEST FAILED')
`,
"/Users/user/project/node_modules/demo-pkg/index-module.js": /* js */ `
- export const foo = 123
+ export const foo = "TEST FAILED"
console.log('TEST FAILED')
`,
"/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
@@ -381,9 +423,12 @@ describe("bundler", () => {
}
`,
},
+ dce: true,
+ run: {
+ stdout: "unused import",
+ },
});
itBundled("dce/PackageJsonSideEffectsArrayKeepMainImplicitMain", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import {foo} from "demo-pkg"
@@ -395,11 +440,11 @@ describe("bundler", () => {
require('demo-pkg')
`,
"/Users/user/project/node_modules/demo-pkg/index-main.js": /* js */ `
- export const foo = 123
+ export const foo = "POSSIBLE_REMOVAL"
console.log('this should be kept')
`,
"/Users/user/project/node_modules/demo-pkg/index-module.js": /* js */ `
- export const foo = 123
+ export const foo = "TEST FAILED"
console.log('TEST FAILED')
`,
"/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
@@ -410,20 +455,23 @@ describe("bundler", () => {
}
`,
},
+ dce: true,
+ run: {
+ stdout: "this should be kept\nunused import",
+ },
});
itBundled("dce/PackageJsonSideEffectsArrayKeepModuleUseModule", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import {foo} from "demo-pkg"
console.log('unused import')
`,
"/Users/user/project/node_modules/demo-pkg/index-main.js": /* js */ `
- export const foo = 123
+ export const foo = "TEST FAILED"
console.log('TEST FAILED')
`,
"/Users/user/project/node_modules/demo-pkg/index-module.js": /* js */ `
- export const foo = 123
+ export const foo = "TEST FAILED"
console.log('this should be kept')
`,
"/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
@@ -434,9 +482,12 @@ describe("bundler", () => {
}
`,
},
+ dce: true,
+ run: {
+ stdout: "this should be kept\nunused import",
+ },
});
itBundled("dce/PackageJsonSideEffectsArrayKeepModuleUseMain", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import {foo} from "demo-pkg"
@@ -448,7 +499,7 @@ describe("bundler", () => {
`,
"/Users/user/project/node_modules/demo-pkg/index-module.js": /* js */ `
export const foo = 123
- console.log('TEST FAILED')
+ console.log('hello')
`,
"/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
{
@@ -458,9 +509,12 @@ describe("bundler", () => {
}
`,
},
+ dce: true,
+ run: {
+ stdout: "hello\nunused import",
+ },
});
itBundled("dce/PackageJsonSideEffectsArrayKeepModuleImplicitModule", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import {foo} from "demo-pkg"
@@ -482,9 +536,12 @@ describe("bundler", () => {
}
`,
},
+ dce: true,
+ run: {
+ stdout: "this should be kept\nunused import",
+ },
});
itBundled("dce/PackageJsonSideEffectsArrayKeepModuleImplicitMain", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import {foo} from "demo-pkg"
@@ -496,11 +553,11 @@ describe("bundler", () => {
require('demo-pkg')
`,
"/Users/user/project/node_modules/demo-pkg/index-main.js": /* js */ `
- export const foo = 123
+ export const foo = "POSSIBLE_REMOVAL"
console.log('this should be kept')
`,
"/Users/user/project/node_modules/demo-pkg/index-module.js": /* js */ `
- export const foo = 123
+ export const foo = "TEST FAILED"
console.log('TEST FAILED')
`,
"/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
@@ -511,9 +568,12 @@ describe("bundler", () => {
}
`,
},
+ dce: true,
+ run: {
+ stdout: "this should be kept\nunused import",
+ },
});
itBundled("dce/PackageJsonSideEffectsArrayGlob", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import "demo-pkg/keep/this/file"
@@ -531,12 +591,12 @@ describe("bundler", () => {
}
`,
},
- /* TODO FIX expectedScanLog: `Users/user/project/src/entry.js: WARNING: Ignoring this import because "Users/user/project/node_modules/demo-pkg/remove/this/file.js" was marked as having no side effects
- Users/user/project/node_modules/demo-pkg/package.json: NOTE: It was excluded from the "sideEffects" array in the enclosing "package.json" file:
- `, */
+ dce: true,
+ run: {
+ stdout: "this should be kept",
+ },
});
itBundled("dce/PackageJsonSideEffectsNestedDirectoryRemove", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import {foo} from "demo-pkg/a/b/c"
@@ -548,13 +608,16 @@ describe("bundler", () => {
}
`,
"/Users/user/project/node_modules/demo-pkg/a/b/c/index.js": /* js */ `
- export const foo = 123
- console.log('hello')
+ export const foo = "TEST FAILED"
+ console.log('TEST FAILED')
`,
},
+ dce: true,
+ run: {
+ stdout: "unused import",
+ },
});
itBundled("dce/PackageJsonSideEffectsKeepExportDefaultExpr", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import foo from "demo-pkg"
@@ -567,9 +630,18 @@ describe("bundler", () => {
}
`,
},
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ globalThis.exprWithSideEffects = () => 1;
+ await import('./out');
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: "1",
+ },
});
itBundled("dce/PackageJsonSideEffectsFalseNoWarningInNodeModulesIssue999", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import "demo-pkg"
@@ -580,8 +652,8 @@ describe("bundler", () => {
console.log('unused import')
`,
"/Users/user/project/node_modules/demo-pkg2/index.js": /* js */ `
- export const foo = 123
- console.log('hello')
+ export const foo = "FAILED"
+ console.log('FAILED')
`,
"/Users/user/project/node_modules/demo-pkg2/package.json": /* json */ `
{
@@ -589,9 +661,12 @@ describe("bundler", () => {
}
`,
},
+ dce: true,
+ run: {
+ stdout: "unused import\nused import",
+ },
});
itBundled("dce/PackageJsonSideEffectsFalseIntermediateFilesUnused", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": `import {foo} from "demo-pkg"`,
"/Users/user/project/node_modules/demo-pkg/index.js": /* js */ `
@@ -601,9 +676,12 @@ describe("bundler", () => {
"/Users/user/project/node_modules/demo-pkg/foo.js": `export const foo = 123`,
"/Users/user/project/node_modules/demo-pkg/package.json": `{ "sideEffects": false }`,
},
+ dce: true,
+ onAfterBundle(api) {
+ api.expectFile("/out.js").toBe("");
+ },
});
itBundled("dce/PackageJsonSideEffectsFalseIntermediateFilesUsed", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import {foo} from "demo-pkg"
@@ -611,11 +689,15 @@ describe("bundler", () => {
`,
"/Users/user/project/node_modules/demo-pkg/index.js": /* js */ `
export {foo} from "./foo.js"
- throw 'keep this'
+ console.log('hello')
`,
"/Users/user/project/node_modules/demo-pkg/foo.js": `export const foo = 123`,
"/Users/user/project/node_modules/demo-pkg/package.json": `{ "sideEffects": false }`,
},
+ dce: true,
+ run: {
+ stdout: "hello\n123",
+ },
});
itBundled("dce/PackageJsonSideEffectsFalseIntermediateFilesChainAll", {
// GENERATED
@@ -628,7 +710,7 @@ describe("bundler", () => {
"/Users/user/project/node_modules/a/package.json": `{ "sideEffects": false }`,
"/Users/user/project/node_modules/b/index.js": /* js */ `
export {foo} from "c"
- throw 'keep this'
+ console.log('hello')
`,
"/Users/user/project/node_modules/b/package.json": `{ "sideEffects": false }`,
"/Users/user/project/node_modules/c/index.js": `export {foo} from "d"`,
@@ -636,9 +718,12 @@ describe("bundler", () => {
"/Users/user/project/node_modules/d/index.js": `export const foo = 123`,
"/Users/user/project/node_modules/d/package.json": `{ "sideEffects": false }`,
},
+ dce: true,
+ run: {
+ stdout: "hello\n123",
+ },
});
itBundled("dce/PackageJsonSideEffectsFalseIntermediateFilesChainOne", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import {foo} from "a"
@@ -647,15 +732,18 @@ describe("bundler", () => {
"/Users/user/project/node_modules/a/index.js": `export {foo} from "b"`,
"/Users/user/project/node_modules/b/index.js": /* js */ `
export {foo} from "c"
- throw 'keep this'
+ console.log('hello')
`,
"/Users/user/project/node_modules/b/package.json": `{ "sideEffects": false }`,
"/Users/user/project/node_modules/c/index.js": `export {foo} from "d"`,
"/Users/user/project/node_modules/d/index.js": `export const foo = 123`,
},
+ dce: true,
+ run: {
+ stdout: "hello\n123",
+ },
});
itBundled("dce/PackageJsonSideEffectsFalseIntermediateFilesDiamond", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import {foo} from "a"
@@ -667,22 +755,25 @@ describe("bundler", () => {
`,
"/Users/user/project/node_modules/b1/index.js": /* js */ `
export {foo} from "c"
- throw 'keep this 1'
+ console.log('hello 1')
`,
"/Users/user/project/node_modules/b1/package.json": `{ "sideEffects": false }`,
"/Users/user/project/node_modules/b2/index.js": /* js */ `
export {foo} from "c"
- throw 'keep this 2'
+ console.log('hello 2')
`,
"/Users/user/project/node_modules/b2/package.json": `{ "sideEffects": false }`,
"/Users/user/project/node_modules/c/index.js": `export {foo} from "d"`,
"/Users/user/project/node_modules/d/index.js": `export const foo = 123`,
},
+ dce: true,
+ run: {
+ stdout: "hello 1\nhello 2\n123",
+ },
});
itBundled("dce/PackageJsonSideEffectsFalseOneFork", {
- // GENERATED
files: {
- "/Users/user/project/src/entry.js": `import("a").then(x => assert(x.foo === "foo"))`,
+ "/Users/user/project/src/entry.js": `import("a").then(x => console.log(x.foo))`,
"/Users/user/project/node_modules/a/index.js": `export {foo} from "b"`,
"/Users/user/project/node_modules/b/index.js": /* js */ `
export {foo, bar} from "c"
@@ -695,11 +786,14 @@ describe("bundler", () => {
`,
"/Users/user/project/node_modules/d/index.js": `export let baz = "baz"`,
},
+ dce: true,
+ run: {
+ stdout: "foo",
+ },
});
itBundled("dce/PackageJsonSideEffectsFalseAllFork", {
- // GENERATED
files: {
- "/Users/user/project/src/entry.js": `import("a").then(x => assert(x.foo === "foo"))`,
+ "/Users/user/project/src/entry.js": `import("a").then(x => console.log(x.foo))`,
"/Users/user/project/node_modules/a/index.js": `export {foo} from "b"`,
"/Users/user/project/node_modules/b/index.js": /* js */ `
export {foo, bar} from "c"
@@ -714,70 +808,102 @@ describe("bundler", () => {
"/Users/user/project/node_modules/d/index.js": `export let baz = "baz"`,
"/Users/user/project/node_modules/d/package.json": `{ "sideEffects": false }`,
},
+ dce: true,
+ run: {
+ stdout: "foo",
+ },
});
itBundled("dce/JSONLoaderRemoveUnused", {
- // GENERATED
files: {
"/entry.js": /* js */ `
import unused from "./example.json"
console.log('unused import')
`,
- "/example.json": `{"data": true}`,
+ "/example.json": `{"data": "FAILED"}`,
+ },
+ dce: true,
+ run: {
+ stdout: "unused import",
},
});
itBundled("dce/TextLoaderRemoveUnused", {
- // GENERATED
files: {
"/entry.js": /* js */ `
import unused from "./example.txt"
console.log('unused import')
`,
- "/example.txt": `some data`,
+ "/example.txt": `TEST FAILED`,
+ },
+ dce: true,
+ run: {
+ stdout: "unused import",
},
});
itBundled("dce/Base64LoaderRemoveUnused", {
- // GENERATED
files: {
"/entry.js": /* js */ `
import unused from "./example.data"
console.log('unused import')
`,
- "/example.data": `some data`,
+ "/example.data": `TEST FAILED`,
+ },
+ dce: true,
+ run: {
+ stdout: "unused import",
+ },
+ loader: {
+ ".data": "base64",
},
});
itBundled("dce/DataURLLoaderRemoveUnused", {
- // GENERATED
files: {
"/entry.js": /* js */ `
import unused from "./example.data"
console.log('unused import')
`,
- "/example.data": `some data`,
+ "/example.data": `TEST FAILED`,
+ },
+ dce: true,
+ run: {
+ stdout: "unused import",
+ },
+ loader: {
+ ".data": "dataurl",
},
});
itBundled("dce/FileLoaderRemoveUnused", {
- // GENERATED
files: {
"/entry.js": /* js */ `
import unused from "./example.data"
console.log('unused import')
`,
- "/example.data": `some data`,
+ "/example.data": `TEST FAILED`,
+ },
+ dce: true,
+ run: {
+ stdout: "unused import",
+ },
+ loader: {
+ ".data": "file",
},
});
itBundled("dce/RemoveUnusedImportMeta", {
- // GENERATED
files: {
"/entry.js": /* js */ `
function foo() {
- console.log(import.meta.url, import.meta.path)
+ console.log(import.meta.url, import.meta.path, 'FAILED')
}
console.log('foo is unused')
`,
},
+ dce: true,
+ run: {
+ stdout: "foo is unused",
+ },
});
itBundled("dce/RemoveUnusedPureCommentCalls", {
- // GENERATED
+ // in this test, the bundler must drop all `_yes` variables entirely, and then
+ // preserve the pure comments in the same way esbuild does
files: {
"/entry.js": /* js */ `
function bar() {}
@@ -841,54 +967,100 @@ describe("bundler", () => {
let new_exp_no = /* @__PURE__ */ new foo() ** foo();
`,
},
+ onAfterBundle(api) {
+ const code = api.readFile("/out.js");
+ assert(!code.includes("_yes"), "should not contain any *_yes variables");
+ assert(code.includes("var bare = foo(bar)"), "should contain `var bare = foo(bar)`");
+ const keep = [
+ ["at_no", true],
+ ["new_at_no", true],
+ ["nospace_at_no", true],
+ ["nospace_new_at_no", true],
+ ["num_no", true],
+ ["new_num_no", true],
+ ["nospace_num_no", true],
+ ["nospace_new_num_no", true],
+ ["dot_no", true],
+ ["new_dot_no", true],
+ ["nested_no", true],
+ ["new_nested_no", true],
+ ["single_at_no", true],
+ ["new_single_at_no", true],
+ ["single_num_no", true],
+ ["new_single_num_no", true],
+ ["bad_no", false],
+ ["new_bad_no", false],
+ ["parens_no", false],
+ ["new_parens_no", false],
+ ["exp_no", true],
+ ["new_exp_no", true],
+ ];
+ for (const [name, pureComment] of keep) {
+ const regex = new RegExp(`${name}\\s*=[^\/\n]*(\\/\\*.*?\\*\\/)?`, "g");
+ const match = regex.exec(code);
+ assert(!!match, `should contain ${name}`);
+ assert(pureComment ? !!match[1] : !match[1], `should contain a pure comment for ${name}`);
+ }
+ },
});
itBundled("dce/TreeShakingReactElements", {
- // GENERATED
files: {
"/entry.jsx": /* jsx */ `
function Foo() {}
- let a = <div/>
- let b = <Foo>{a}</Foo>
- let c = <>{b}</>
+ let DROP_a = <div/>
+ let DROP_b = <Foo>{DROP_a}</Foo>
+ let DROP_c = <>{DROP_b}</>
let d = <div/>
let e = <Foo>{d}</Foo>
let f = <>{e}</>
- console.log(f)
- `,
- },
- });
- itBundled("dce/DisableTreeShaking", {
- // GENERATED
- files: {
- "/entry.jsx": /* jsx */ `
- import './remove-me'
- function RemoveMe1() {}
- let removeMe2 = 0
- class RemoveMe3 {}
-
- import './keep-me'
- function KeepMe1() {}
- let keepMe2 = <KeepMe1/>
- function keepMe3() { console.log('side effects') }
- let keepMe4 = /* @__PURE__ */ keepMe3()
- let keepMe5 = pure()
- let keepMe6 = some.fn()
+ console.log(JSON.stringify(f))
`,
- "/remove-me.js": `export default 'unused'`,
- "/keep-me/index.js": `console.log('side effects')`,
- "/keep-me/package.json": `{ "sideEffects": false }`,
- },
- // TODO: Unsure how to port this: https://github.com/evanw/esbuild/blob/main/internal/bundler_tests/bundler_dce_test.go#L1249
- ignoreDCEAnnotations: true,
- define: {
- pure: "???",
- "some.fn": "???",
- },
- });
+
+ "/node_modules/react/index.js": `export const Fragment = 'F'`,
+ "/node_modules/react/jsx-dev-runtime.js": `export const jsxDEV = (a,b) => [a,b]; export const Fragment = 'F'`,
+ },
+ jsx: {
+ development: true,
+ automaticRuntime: true,
+ },
+ dce: true,
+ run: {
+ stdout: `["F",{"children":[null,{"children":["div",{}]}]}]`,
+ },
+ });
+ // itBundled("dce/DisableTreeShaking", {
+ // // GENERATED
+ // files: {
+ // "/entry.jsx": /* jsx */ `
+ // import './remove-me'
+ // function RemoveMe1() {}
+ // let removeMe2 = 0
+ // class RemoveMe3 {}
+
+ // import './keep-me'
+ // function KeepMe1() {}
+ // let keepMe2 = <KeepMe1/>
+ // function keepMe3() { console.log('side effects') }
+ // let keepMe4 = /* @__PURE__ */ keepMe3()
+ // let keepMe5 = pure()
+ // let keepMe6 = some.fn()
+ // `,
+ // "/remove-me.js": `export default 'unused'`,
+ // "/keep-me/index.js": `console.log('side effects')`,
+ // "/keep-me/package.json": `{ "sideEffects": false }`,
+ // },
+ // // TODO: Unsure how to port this: https://github.com/evanw/esbuild/blob/main/internal/bundler_tests/bundler_dce_test.go#L1249
+ // ignoreDCEAnnotations: true,
+ // define: {
+ // pure: "???",
+ // "some.fn": "???",
+ // },
+ // });
+ 0; // the commented out test has a pure comment which unless this 0 line is here, that test will be removed
+
itBundled("dce/DeadCodeFollowingJump", {
- // GENERATED
files: {
"/entry.js": /* js */ `
function testReturn() {
@@ -971,9 +1143,10 @@ describe("bundler", () => {
testStmts()
`,
},
+ dce: true,
+ minifySyntax: true,
});
itBundled("dce/RemoveTrailingReturn", {
- // GENERATED
files: {
"/entry.js": /* js */ `
function foo() {
@@ -1007,9 +1180,16 @@ describe("bundler", () => {
`,
},
minifySyntax: true,
+ dce: true,
+ onAfterBundle(api) {
+ const code = api.readFile("/out.js");
+ assert(
+ [...code.matchAll(/return/g)].length === 2,
+ "should remove 3 trailing returns and the arrow function return",
+ );
+ },
});
itBundled("dce/ImportReExportOfNamespaceImport", {
- // GENERATED
files: {
"/Users/user/project/entry.js": /* js */ `
import * as ns from 'pkg'
@@ -1021,26 +1201,33 @@ describe("bundler", () => {
`,
"/Users/user/project/node_modules/pkg/package.json": `{ "sideEffects": false }`,
"/Users/user/project/node_modules/pkg/foo.js": `module.exports = 123`,
- "/Users/user/project/node_modules/pkg/bar.js": `module.exports = 'abc'`,
+ "/Users/user/project/node_modules/pkg/bar.js": `module.exports = 'FAILED'`,
+ },
+ dce: true,
+ run: {
+ stdout: "123",
},
});
itBundled("dce/TreeShakingImportIdentifier", {
- // GENERATED
files: {
"/entry.js": /* js */ `
import * as a from './a'
- new a.Keep()
+ new a.Keep().x().y()
`,
"/a.js": /* js */ `
import * as b from './b'
- export class Keep extends b.Base {}
- export class REMOVE extends b.Base {}
+ export class Keep extends b.Base { y() { console.log(2); return this; } }
+ export class REMOVE extends b.Base { y() { console.log(3); return this; } }
`,
- "/b.js": `export class Base {}`,
+ "/b.js": `export class Base { x() { console.log(1); return this; } }`,
+ },
+ dce: true,
+ dceKeepMarkerCount: false,
+ run: {
+ stdout: "1\n2",
},
});
itBundled("dce/TreeShakingObjectProperty", {
- // GENERATED
files: {
"/entry.js": /* js */ `
let remove1 = { x: 'x' }
@@ -1072,10 +1259,9 @@ describe("bundler", () => {
`,
},
treeShaking: true,
- mode: "passthrough",
+ dce: true,
});
itBundled("dce/TreeShakingClassProperty", {
- // GENERATED
files: {
"/entry.js": /* js */ `
let remove1 = class { x }
@@ -1095,20 +1281,22 @@ describe("bundler", () => {
let remove15 = class { [false] = 'x' }
let remove16 = class { [0n] = 'x' }
let remove17 = class { toString() {} }
-
+
let keep1 = class { [x] = 'x' }
let keep2 = class { [x]() {} }
let keep3 = class { get [x]() {} }
let keep4 = class { set [x](_) {} }
let keep5 = class { async [x]() {} }
- let keep6 = class { [{ toString() {} }] = 'x' }
+ let keep6 = class { [{ toString() { console.log(1); } }] = 'x' }
+
+ let POSSIBLE_REMOVAL_1 = class { [{ toString() {} }] = 'x' }
`,
},
+ mode: "transform",
treeShaking: true,
- mode: "passthrough",
+ dce: true,
});
itBundled("dce/TreeShakingClassStaticProperty", {
- // GENERATED
files: {
"/entry.js": /* js */ `
let remove1 = class { static x }
@@ -1134,14 +1322,16 @@ describe("bundler", () => {
let keep5 = class { static get [x]() {} }
let keep6 = class { static set [x](_) {} }
let keep7 = class { static async [x]() {} }
- let keep8 = class { static [{ toString() {} }] = 'x' }
+ let keep8 = class { static [{ toString() { console.log(1); } }] = 'x' }
+
+ let POSSIBLE_REMOVAL_1 = class { static [{ toString() {} }] = 'x' }
`,
},
+ mode: "transform",
treeShaking: true,
- mode: "passthrough",
+ dce: true,
});
itBundled("dce/TreeShakingUnaryOperators", {
- // GENERATED
files: {
"/entry.js": /* js */ `
// These operators may have side effects
@@ -1161,9 +1351,10 @@ describe("bundler", () => {
void REMOVE;
`,
},
+ format: "iife",
+ dce: true,
});
itBundled("dce/TreeShakingBinaryOperators", {
- // GENERATED
files: {
"/entry.js": /* js */ `
// These operators may have side effects
@@ -1215,30 +1406,34 @@ describe("bundler", () => {
REMOVE && REMOVE2;
`,
},
+ dce: true,
+ format: "iife",
});
itBundled("dce/TreeShakingNoBundleESM", {
- // GENERATED
files: {
"/entry.js": /* js */ `
function keep() {}
- function unused() {}
+ function REMOVE() {}
keep()
`,
},
format: "esm",
- mode: "convertformat",
+ mode: "transform",
+ treeShaking: true,
+ dce: true,
});
itBundled("dce/TreeShakingNoBundleCJS", {
// GENERATED
files: {
"/entry.js": /* js */ `
function keep() {}
- function unused() {}
+ function REMOVE() {}
keep()
`,
},
format: "cjs",
- mode: "convertformat",
+ treeShaking: true,
+ mode: "transform",
});
itBundled("dce/TreeShakingNoBundleIIFE", {
// GENERATED
@@ -1250,14 +1445,14 @@ describe("bundler", () => {
`,
},
format: "iife",
- mode: "convertformat",
+ treeShaking: true,
+ mode: "transform",
});
itBundled("dce/TreeShakingInESMWrapper", {
- // GENERATED
files: {
"/entry.js": /* js */ `
import {keep1} from './lib'
- console.log(keep1(), require('./cjs'))
+ console.log(JSON.stringify([keep1(), require('./cjs')]))
`,
"/cjs.js": /* js */ `
import {keep2} from './lib'
@@ -1270,6 +1465,11 @@ describe("bundler", () => {
`,
},
format: "esm",
+ dce: true,
+ dceKeepMarkerCount: false,
+ run: {
+ stdout: '["keep1",{"default":"keep2"}]',
+ },
});
itBundled("dce/DCETypeOf", {
// GENERATED
@@ -1286,7 +1486,7 @@ describe("bundler", () => {
function* g_REMOVE() {}
async function a_REMOVE() {}
- // These technically have side effects due to TDZ, but this is not currently handled
+ // TODO: These technically have side effects due to TDZ, but this is not currently handled
typeof c_remove
typeof l_remove
typeof s_remove
@@ -1296,31 +1496,31 @@ describe("bundler", () => {
`,
},
format: "esm",
+ dce: true,
});
itBundled("dce/DCETypeOfEqualsString", {
- // GENERATED
files: {
"/entry.js": /* js */ `
- var hasBar = typeof bar !== 'undefined'
+ var hasBar = typeof REMOVE !== 'undefined'
if (false) console.log(hasBar)
`,
},
format: "iife",
+ dce: true,
});
itBundled("dce/DCETypeOfEqualsStringMangle", {
- // GENERATED
files: {
"/entry.js": /* js */ `
// Everything here should be removed as dead code due to tree shaking
- var hasBar = typeof bar !== 'undefined'
- if (false) console.log(hasBar)
+ var REMOVE1 = typeof REMOVE2 !== 'undefined'
+ if (false) console.log(REMOVE1)
`,
},
format: "iife",
minifySyntax: true,
+ dce: true,
});
itBundled("dce/DCETypeOfEqualsStringGuardCondition", {
- // GENERATED
files: {
"/entry.js": /* js */ `
// Everything here should be removed as dead code due to tree shaking
@@ -1415,9 +1615,9 @@ describe("bundler", () => {
`,
},
format: "iife",
+ dce: true,
});
itBundled("dce/DCETypeOfCompareStringGuardCondition", {
- // GENERATED
files: {
"/entry.js": /* js */ `
// Everything here should be removed as dead code due to tree shaking
@@ -1476,48 +1676,56 @@ describe("bundler", () => {
`,
},
format: "iife",
+ dce: true,
});
itBundled("dce/RemoveUnusedImports", {
- // GENERATED
files: {
"/entry.js": /* js */ `
- import a from 'a'
- import * as b from 'b'
- import {c} from 'c'
+ import REMOVE1 from 'a'
+ import * as REMOVE2 from 'b'
+ import {REMOVE3} from 'c'
`,
},
minifySyntax: true,
- mode: "passthrough",
+ mode: "transform",
+ dce: true,
+ onAfterBundle(api) {
+ api.expectFile("/out.js").toBe(
+ dedent`
+ import "a";
+ import "b";
+ import "c";
+ ` + "\n",
+ );
+ },
});
itBundled("dce/RemoveUnusedImportsEval", {
- // GENERATED
files: {
"/entry.js": /* js */ `
- import a from 'a'
- import * as b from 'b'
- import {c} from 'c'
- eval('foo(a, b, c)')
+ import keep_a from 'a'
+ import * as keep_b from 'b'
+ import {keep_c} from 'c'
+ eval('foo(keep_a, keep_b, keep_c)')
`,
},
minifySyntax: true,
- mode: "passthrough",
+ mode: "transform",
+ dce: true,
});
itBundled("dce/RemoveUnusedImportsEvalTS", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
- import a from 'a'
- import * as b from 'b'
- import {c} from 'c'
+ import drop_a from 'a'
+ import * as drop_b from 'b'
+ import {drop_c} from 'c'
eval('foo(a, b, c)')
`,
},
- entryPoints: ["/entry.js"],
+ dce: true,
minifySyntax: true,
- mode: "passthrough",
+ mode: "transform",
});
itBundled("dce/DCEClassStaticBlocks", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
class A_REMOVE {
@@ -1553,33 +1761,40 @@ describe("bundler", () => {
}
`,
},
- entryPoints: ["/entry.js"],
+ dce: true,
});
itBundled("dce/DCEVarExports", {
- // GENERATED
files: {
"/a.js": /* js */ `
- var foo = { bar: 123 }
+ var foo = { keep: 123 }
module.exports = foo
`,
"/b.js": /* js */ `
- var exports = { bar: 123 }
+ var exports = { keep: 123 }
module.exports = exports
`,
"/c.js": /* js */ `
- var module = { bar: 123 }
+ var module = { keep: 123 }
exports.foo = module
`,
},
+ dce: true,
entryPoints: ["/a.js", "/b.js", "/c.js"],
});
itBundled("dce/DCETemplateLiteral", {
- // GENERATED
- files: {},
- entryPoints: ["/entry.js"],
+ files: {
+ "/entry.js":
+ "var remove;\n" +
+ "var alsoKeep;\n" +
+ "let a = `${keep}`\n" +
+ "let remove2 = `${123}`\n" +
+ "let c = `${keep ? 1 : 2n}`\n" +
+ "let remove3 = `${remove ? 1 : 2n}`\n" +
+ "let e = `${alsoKeep}`\n",
+ },
+ dce: true,
});
itBundled("dce/TreeShakingLoweredClassStaticField", {
- // GENERATED
files: {
"/entry.js": /* js */ `
class REMOVE_ME {
@@ -1603,9 +1818,11 @@ describe("bundler", () => {
new KeepMe2()
`,
},
+ dce: true,
+ dceKeepMarkerCount: 9,
+ unsupportedJSFeatures: ["class-field"],
});
itBundled("dce/TreeShakingLoweredClassStaticFieldMinified", {
- // GENERATED
files: {
"/entry.js": /* js */ `
class REMOVE_ME {
@@ -1629,10 +1846,12 @@ describe("bundler", () => {
new KeepMe2()
`,
},
- unsupportedJSFeatures: "ClassField",
+ dce: true,
+ dceKeepMarkerCount: 9,
+ unsupportedJSFeatures: ["class-field"],
+ minifySyntax: true,
});
itBundled("dce/TreeShakingLoweredClassStaticFieldAssignment", {
- // GENERATED
files: {
"/entry.ts": /* ts */ `
class KeepMe1 {
@@ -1642,7 +1861,7 @@ describe("bundler", () => {
}
class KeepMe2 {
static x = 'x'
- static y = sideEffects()
+ static y = sideEffects_keep()
static z = 'z'
}
class KeepMe3 {
@@ -1653,11 +1872,11 @@ describe("bundler", () => {
new KeepMe3()
`,
},
- entryPoints: ["/entry.js"],
- unsupportedJSFeatures: "ClassField",
+ unsupportedJSFeatures: ["class-field"],
+ dce: true,
+ dceKeepMarkerCount: 14,
});
itBundled("dce/InlineIdentityFunctionCalls", {
- // GENERATED
files: {
"/identity.js": /* js */ `
function DROP(x) { return x }
@@ -1801,9 +2020,13 @@ describe("bundler", () => {
"/not-identity-rest.js",
"/not-identity-return.js",
],
+ dce: true,
+ minifySyntax: true,
+ dceKeepMarkerCount: {
+ "/out/identity-first.js": 4,
+ },
});
itBundled("dce/InlineEmptyFunctionCalls", {
- // GENERATED
files: {
"/empty.js": /* js */ `
function DROP() {}
@@ -1914,49 +2137,93 @@ describe("bundler", () => {
"/reassign-array.js",
"/reassign-object.js",
],
+ minifySyntax: true,
+ dce: true,
+ dceKeepMarkerCount: {
+ "/out/empty-first.js": 4,
+ },
});
itBundled("dce/InlineFunctionCallBehaviorChanges", {
- // GENERATED
files: {
- "/entry.js": `
- function empty() {}
- function id(x) { return x }
-
- export let shouldBeWrapped = [
- id(foo.bar)(),
- id(foo[bar])(),
- id(foo?.bar)(),
- id(foo?.[bar])(),
-
- (empty(), foo.bar)(),
- (empty(), foo[bar])(),
- (empty(), foo?.bar)(),
- (empty(), foo?.[bar])(),
-
- id(eval)(),
- id(eval)?.(),
- (empty(), eval)(),
- (empty(), eval)?.(),
-
- id(foo.bar)\` + "\`\`" +
- `,
- },
- mode: "passthrough",
+ // At the time of writing, using a template string here triggered a bug in bun's transpiler
+ // making it impossible to run the test.
+ "/entry.js":
+ "function empty_REMOVE() { }\n" +
+ "function id_REMOVE(x) { return x }\n" +
+ "\n" +
+ "export let shouldBeWrapped = [\n" +
+ " id_REMOVE(foo.bar)(),\n" +
+ " id_REMOVE(foo[bar])(),\n" +
+ " id_REMOVE(foo?.bar)(),\n" +
+ " id_REMOVE(foo?.[bar])(),\n" +
+ "\n" +
+ " (empty_REMOVE(), foo.bar)(),\n" +
+ " (empty_REMOVE(), foo[bar])(),\n" +
+ " (empty_REMOVE(), foo?.bar)(),\n" +
+ " (empty_REMOVE(), foo?.[bar])(),\n" +
+ "\n" +
+ " id_REMOVE(eval)(),\n" +
+ " id_REMOVE(eval)?.(),\n" +
+ " (empty_REMOVE(), eval)(),\n" +
+ " (empty_REMOVE(), eval)?.(),\n" +
+ "\n" +
+ " id_REMOVE(foo.bar)``,\n" +
+ " id_REMOVE(foo[bar])``,\n" +
+ " id_REMOVE(foo?.bar)``,\n" +
+ " id_REMOVE(foo?.[bar])``,\n" +
+ "\n" +
+ " (empty_REMOVE(), foo.bar)``,\n" +
+ " (empty_REMOVE(), foo[bar])``,\n" +
+ " (empty_REMOVE(), foo?.bar)``,\n" +
+ " (empty_REMOVE(), foo?.[bar])``,\n" +
+ "\n" +
+ " delete id_REMOVE(foo),\n" +
+ " delete id_REMOVE(foo.bar),\n" +
+ " delete id_REMOVE(foo[bar]),\n" +
+ " delete id_REMOVE(foo?.bar),\n" +
+ " delete id_REMOVE(foo?.[bar]),\n" +
+ "\n" +
+ " delete (empty_REMOVE(), foo),\n" +
+ " delete (empty_REMOVE(), foo.bar),\n" +
+ " delete (empty_REMOVE(), foo[bar]),\n" +
+ " delete (empty_REMOVE(), foo?.bar),\n" +
+ " delete (empty_REMOVE(), foo?.[bar]),\n" +
+ "\n" +
+ " delete empty_REMOVE(),\n" +
+ "]\n" +
+ "\n" +
+ "export let shouldNotBeWrapped = [\n" +
+ " id_REMOVE(foo)(),\n" +
+ " (empty_REMOVE(), foo)(),\n" +
+ "\n" +
+ " id_REMOVE(foo)``,\n" +
+ " (empty_REMOVE(), foo)``,\n" +
+ "]\n" +
+ "\n" +
+ "export let shouldNotBeDoubleWrapped = [\n" +
+ " delete (empty_REMOVE(), foo(), foo()),\n" +
+ " delete id_REMOVE((foo(), bar())),\n" +
+ "]",
+ },
+ mode: "transform",
+ minifySyntax: true,
+ treeShaking: true,
+ dce: true,
});
itBundled("dce/InlineFunctionCallForInitDecl", {
- // GENERATED
files: {
"/entry.js": /* js */ `
- function empty() {}
- function id(x) { return x }
+ function empty_REMOVE() {}
+ function id_REMOVE(x) { return x }
- for (var y = empty(); false; ) ;
- for (var z = id(123); false; ) ;
+ for (var y = empty_REMOVE(); false; ) ;
+ for (var z = id_REMOVE(123); false; ) ;
`,
},
+ minifySyntax: true,
+ dce: true,
});
itBundled("dce/ConstValueInliningNoBundle", {
- // GENERATED
files: {
"/top-level.js": /* js */ `
// These should be kept because they are top-level and tree shaking is not enabled
@@ -2105,10 +2372,15 @@ describe("bundler", () => {
"/backwards-reference-top-level.js",
"/backwards-reference-nested-function.js",
],
- mode: "passthrough",
+ mode: "transform",
+ minifySyntax: true,
+ dce: true,
+ dceKeepMarkerCount: {
+ "/out/top-level.js": 7,
+ "/out/namespace-export.js": 1,
+ },
});
itBundled("dce/ConstValueInliningBundle", {
- // GENERATED
files: {
"/exported-entry.js": /* js */ `
const x_REMOVE = 1
@@ -2226,9 +2498,15 @@ describe("bundler", () => {
],
format: "esm",
minifySyntax: true,
+ dce: true,
+ dceKeepMarkerCount: {
+ "/out/re-exported-entry.js": 2,
+ "/out/re-exported-2-entry.js": 2,
+ "/out/re-exported-star-entry.js": 4,
+ "/out/re-exported-star-entry.js.": 4,
+ },
});
itBundled("dce/ConstValueInliningAssign", {
- // GENERATED
files: {
"/const-assign.js": /* js */ `
const x = 1
@@ -2240,46 +2518,43 @@ describe("bundler", () => {
`,
},
entryPoints: ["/const-assign.js", "/const-update.js"],
- mode: "passthrough",
- /* TODO FIX expectedScanLog: `const-assign.js: ERROR: Cannot assign to "x" because it is a constant
- const-assign.js: NOTE: The symbol "x" was declared a constant here:
- const-update.js: ERROR: Cannot assign to "x" because it is a constant
- const-update.js: NOTE: The symbol "x" was declared a constant here:
- `, */
+ bundleErrors: {
+ "/const-assign.js": ['Cannot assign to constant variable "x"'],
+ "/const-update.js": ['Cannot assign to constant variable "x"'],
+ },
});
itBundled("dce/ConstValueInliningDirectEval", {
- // GENERATED
files: {
"/top-level-no-eval.js": /* js */ `
- const x = 1
- console.log(x, evil('x'))
+ const keep = 1
+ console.log(keep, evil('x')) // inline the 1 here
`,
"/top-level-eval.js": /* js */ `
- const x = 1
- console.log(x, eval('x'))
+ const keep = 1
+ console.log(keep, eval('x')) // inline the 1 but keep the const def
`,
"/nested-no-eval.js": /* js */ `
(() => {
- const x = 1
- console.log(x, evil('x'))
+ const remove = 1
+ console.log(remove, evil('x')) // inline the 1 here and remove the const def
})()
`,
"/nested-eval.js": /* js */ `
(() => {
- const x = 1
- console.log(x, eval('x'))
+ const keep = 1
+ console.log(keep, eval('x')) // inline the 1 but keep the const def
})()
`,
"/ts-namespace-no-eval.ts": /* ts */ `
namespace y {
- export const x = 1
- console.log(x, evil('x'))
+ export const keep = 1
+ console.log(keep, evil('x')) // inline the 1 here
}
`,
"/ts-namespace-eval.ts": /* ts */ `
namespace z {
- export const x = 1
- console.log(x, eval('x'))
+ export const keep = 1
+ console.log(keep, eval('x'))
}
`,
},
@@ -2291,19 +2566,28 @@ describe("bundler", () => {
"/ts-namespace-no-eval.ts",
"/ts-namespace-eval.ts",
],
- mode: "passthrough",
+ mode: "transform",
+ minifySyntax: true,
+ dce: true,
+ dceKeepMarkerCount: {
+ "/out/top-level-no-eval.js": 1,
+ "/out/top-level-eval.js": 1,
+ "/out/nested-eval.js": 1,
+ "/out/ts-namespace-no-eval.js": 1,
+ "/out/ts-namespace-eval.js": 1,
+ },
});
itBundled("dce/CrossModuleConstantFolding", {
// GENERATED
files: {
"/enum-constants.ts": /* ts */ `
- export enum x {
+ export enum remove {
a = 3,
b = 6,
}
`,
"/enum-entry.ts": /* ts */ `
- import { x } from './enum-constants'
+ import { remove as x } from './enum-constants'
console.log([
+x.b,
-x.b,
@@ -2386,14 +2670,14 @@ describe("bundler", () => {
export const a = 2
export const b = 4
export const c = 8
- export enum x {
+ export enum remove {
a = 16,
b = 32,
c = 64,
}
`,
"/nested-entry.ts": /* ts */ `
- import { a, b, c, x } from './nested-constants'
+ import { a, b, c, remove as x } from './nested-constants'
console.log({
'should be 4': ~(~a & ~b) & (b | c),
'should be 32': ~(~x.a & ~x.b) & (x.b | x.c),
@@ -2401,9 +2685,9 @@ describe("bundler", () => {
`,
},
entryPoints: ["/enum-entry.ts", "/const-entry.js", "/nested-entry.ts"],
+ dce: true,
});
itBundled("dce/MultipleDeclarationTreeShaking", {
- // GENERATED
files: {
"/var2.js": /* js */ `
var x = 1
@@ -2431,9 +2715,17 @@ describe("bundler", () => {
`,
},
entryPoints: ["/var2.js", "/var3.js", "/function2.js", "/function3.js"],
+ dce: true,
+ treeShaking: true,
+ minifySyntax: false,
+ run: [
+ { file: "/out/var2.js", stdout: "1" },
+ { file: "/out/var3.js", stdout: "1\n2" },
+ { file: "/out/function2.js", stdout: "2" },
+ { file: "/out/function3.js", stdout: "3\n3" },
+ ],
});
itBundled("dce/MultipleDeclarationTreeShakingMinifySyntax", {
- // GENERATED
files: {
"/var2.js": /* js */ `
var x = 1
@@ -2448,79 +2740,118 @@ describe("bundler", () => {
var x = 3
`,
"/function2.js": /* js */ `
- function x() { return 1 }
+ function x() { return "REMOVE" }
console.log(x())
function x() { return 2 }
`,
"/function3.js": /* js */ `
- function x() { return 1 }
+ function x() { return "REMOVE" }
console.log(x())
- function x() { return 2 }
+ function x() { return "REMOVE" }
console.log(x())
function x() { return 3 }
`,
},
entryPoints: ["/var2.js", "/var3.js", "/function2.js", "/function3.js"],
+ dce: true,
+ treeShaking: true,
+ minifySyntax: true,
+ run: [
+ { file: "/out/var2.js", stdout: "1" },
+ { file: "/out/var3.js", stdout: "1\n2" },
+ { file: "/out/function2.js", stdout: "2" },
+ { file: "/out/function3.js", stdout: "3\n3" },
+ ],
});
itBundled("dce/PureCallsWithSpread", {
- // GENERATED
files: {
+ // this changes to "[...args]"
"/entry.js": /* js */ `
- /* @__PURE__ */ foo(...args);
- /* @__PURE__ */ new foo(...args);
+ /* @__PURE__ */ REMOVE(...args);
+ /* @__PURE__ */ new REMOVE(...args);
`,
},
+ minifySyntax: true,
+ dce: true,
+ onAfterBundle(api) {
+ const code = api.readFile("/out.js");
+ assert([...code.matchAll(/\[\.\.\.args\]/g)].length === 2, "spread should be preserved");
+ },
});
itBundled("dce/TopLevelFunctionInliningWithSpread", {
- // GENERATED
files: {
"/entry.js": /* js */ `
- function empty1() {}
- function empty2() {}
- function empty3() {}
+ function empty1_remove() {}
+ function empty2_remove() {}
+ function empty3_remove() {}
function identity1(x) { return x }
- function identity2(x) { return x }
+ function identity2_remove(x) { return x }
function identity3(x) { return x }
- empty1()
- empty2(args)
- empty3(...args)
+ empty1_remove()
+ empty2_remove(args)
+ empty3_remove(...args)
identity1()
- identity2(args)
+ identity2_remove(args)
identity3(...args)
`,
"/inner.js": /* js */ `
- export function empty1() {}
- export function empty2() {}
- export function empty3() {}
+ export function empty1_remove() {}
+ export function empty2_remove() {}
+ export function empty3_remove() {}
export function identity1(x) { return x }
- export function identity2(x) { return x }
+ export function identity2_remove(x) { return x }
export function identity3(x) { return x }
`,
"/entry-outer.js": /* js */ `
import {
- empty1,
- empty2,
- empty3,
+ empty1_remove,
+ empty2_remove,
+ empty3_remove,
identity1,
- identity2,
+ identity2_remove,
identity3,
} from './inner.js'
- empty1()
- empty2(args)
- empty3(...args)
+ empty1_remove()
+ empty2_remove(args)
+ empty3_remove(...args)
identity1()
- identity2(args)
+ identity2_remove(args)
identity3(...args)
`,
},
+ dce: true,
entryPoints: ["/entry.js", "/entry-outer.js"],
+ minifySyntax: true,
+
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ globalThis.args = {
+ [Symbol.iterator]() {
+ console.log('spread')
+ return {
+ next() {
+ return { done: true, value: undefined }
+ }
+ }
+ }
+ };
+
+ await import('./out/entry.js');
+ console.log('---')
+ await import('./out/entry-outer.js');
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: "spread\nspread\n---\nspread\nspread",
+ },
});
itBundled("dce/NestedFunctionInliningWithSpread", {
// GENERATED
@@ -2577,8 +2908,8 @@ describe("bundler", () => {
},
entryPoints: ["/entry.js", "/entry-outer.js"],
});
+ // im confused what this is testing. cross platform slash? there is none?? not even in the go source
itBundled("dce/PackageJsonSideEffectsFalseCrossPlatformSlash", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": /* js */ `
import "demo-pkg/foo"
@@ -2595,155 +2926,159 @@ describe("bundler", () => {
}
`,
},
- });
- itBundled("dce/TreeShakingJSWithAssociatedCSS", {
- // GENERATED
- files: {
- "/project/test.jsx": /* jsx */ `
- import { Button } from 'pkg/button'
- import { Menu } from 'pkg/menu'
- render(<Button/>)
- `,
- "/project/node_modules/pkg/button.js": /* js */ `
- import './button.css'
- export let Button
- `,
- "/project/node_modules/pkg/button.css": `button { color: red }`,
- "/project/node_modules/pkg/menu.js": /* js */ `
- import './menu.css'
- export let Menu
- `,
- "/project/node_modules/pkg/menu.css": `menu { color: red }`,
- },
- });
- itBundled("dce/TreeShakingJSWithAssociatedCSSReExportSideEffectsFalse", {
- // GENERATED
- files: {
- "/project/test.jsx": /* jsx */ `
- import { Button } from 'pkg'
- render(<Button/>)
- `,
- "/project/node_modules/pkg/entry.js": `export { Button } from './components'`,
- "/project/node_modules/pkg/package.json": /* json */ `
- {
- "main": "./entry.js",
- "sideEffects": false
- }
- `,
- "/project/node_modules/pkg/components.jsx": /* jsx */ `
- require('./button.css')
- export const Button = () => <button/>
- `,
- "/project/node_modules/pkg/button.css": `button { color: red }`,
- },
- });
- itBundled("dce/TreeShakingJSWithAssociatedCSSReExportSideEffectsFalseOnlyJS", {
- // GENERATED
- files: {
- "/project/test.jsx": /* jsx */ `
- import { Button } from 'pkg'
- render(<Button/>)
- `,
- "/project/node_modules/pkg/entry.js": `export { Button } from './components'`,
- "/project/node_modules/pkg/package.json": /* json */ `
- {
- "main": "./entry.js",
- "sideEffects": ["*.css"]
- }
- `,
- "/project/node_modules/pkg/components.jsx": /* jsx */ `
- require('./button.css')
- export const Button = () => <button/>
- `,
- "/project/node_modules/pkg/button.css": `button { color: red }`,
- },
- });
- itBundled("dce/TreeShakingJSWithAssociatedCSSExportStarSideEffectsFalse", {
- // GENERATED
- files: {
- "/project/test.jsx": /* jsx */ `
- import { Button } from 'pkg'
- render(<Button/>)
- `,
- "/project/node_modules/pkg/entry.js": `export * from './components'`,
- "/project/node_modules/pkg/package.json": /* json */ `
- {
- "main": "./entry.js",
- "sideEffects": false
- }
- `,
- "/project/node_modules/pkg/components.jsx": /* jsx */ `
- require('./button.css')
- export const Button = () => <button/>
- `,
- "/project/node_modules/pkg/button.css": `button { color: red }`,
- },
- });
- itBundled("dce/TreeShakingJSWithAssociatedCSSExportStarSideEffectsFalseOnlyJS", {
- // GENERATED
- files: {
- "/project/test.jsx": /* jsx */ `
- import { Button } from 'pkg'
- render(<Button/>)
- `,
- "/project/node_modules/pkg/entry.js": `export * from './components'`,
- "/project/node_modules/pkg/package.json": /* json */ `
- {
- "main": "./entry.js",
- "sideEffects": ["*.css"]
- }
- `,
- "/project/node_modules/pkg/components.jsx": /* jsx */ `
- require('./button.css')
- export const Button = () => <button/>
- `,
- "/project/node_modules/pkg/button.css": `button { color: red }`,
- },
- });
- itBundled("dce/TreeShakingJSWithAssociatedCSSUnusedNestedImportSideEffectsFalse", {
- // GENERATED
- files: {
- "/project/test.jsx": /* jsx */ `
- import { Button } from 'pkg/button'
- render(<Button/>)
- `,
- "/project/node_modules/pkg/package.json": /* json */ `
- {
- "sideEffects": false
- }
- `,
- "/project/node_modules/pkg/button.jsx": /* jsx */ `
- import styles from './styles'
- export const Button = () => <button/>
- `,
- "/project/node_modules/pkg/styles.js": /* js */ `
- import './styles.css'
- export default {}
- `,
- "/project/node_modules/pkg/styles.css": `button { color: red }`,
- },
- });
- itBundled("dce/TreeShakingJSWithAssociatedCSSUnusedNestedImportSideEffectsFalseOnlyJS", {
- // GENERATED
- files: {
- "/project/test.jsx": /* jsx */ `
- import { Button } from 'pkg/button'
- render(<Button/>)
- `,
- "/project/node_modules/pkg/package.json": /* json */ `
- {
- "sideEffects": ["*.css"]
- }
- `,
- "/project/node_modules/pkg/button.jsx": /* jsx */ `
- import styles from './styles'
- export const Button = () => <button/>
- `,
- "/project/node_modules/pkg/styles.js": /* js */ `
- import './styles.css'
- export default {}
- `,
- "/project/node_modules/pkg/styles.css": `button { color: red }`,
- },
- });
+ run: {
+ stdout: "foo\nbar",
+ },
+ });
+ // itBundled("dce/TreeShakingJSWithAssociatedCSS", {
+ // // TODO: css assertions. this should contain both button and menu
+ // files: {
+ // "/project/test.jsx": /* jsx */ `
+ // import { Button } from 'pkg/button'
+ // import { Menu } from 'pkg/menu'
+ // render(<Button/>)
+ // `,
+ // "/project/node_modules/pkg/button.js": /* js */ `
+ // import './button.css'
+ // export let Button
+ // `,
+ // "/project/node_modules/pkg/button.css": `button { color: red }`,
+ // "/project/node_modules/pkg/menu.js": /* js */ `
+ // import './menu.css'
+ // export let Menu
+ // `,
+ // "/project/node_modules/pkg/menu.css": `menu { color: green }`,
+ // },
+ // external: ["react"],
+ // });
+ // itBundled("dce/TreeShakingJSWithAssociatedCSSReExportSideEffectsFalse", {
+ // // GENERATED
+ // files: {
+ // "/project/test.jsx": /* jsx */ `
+ // import { Button } from 'pkg'
+ // render(<Button/>)
+ // `,
+ // "/project/node_modules/pkg/entry.js": `export { Button } from './components'`,
+ // "/project/node_modules/pkg/package.json": /* json */ `
+ // {
+ // "main": "./entry.js",
+ // "sideEffects": false
+ // }
+ // `,
+ // "/project/node_modules/pkg/components.jsx": /* jsx */ `
+ // require('./button.css')
+ // export const Button = () => <button/>
+ // `,
+ // "/project/node_modules/pkg/button.css": `button { color: red }`,
+ // },
+ // });
+ // itBundled("dce/TreeShakingJSWithAssociatedCSSReExportSideEffectsFalseOnlyJS", {
+ // // GENERATED
+ // files: {
+ // "/project/test.jsx": /* jsx */ `
+ // import { Button } from 'pkg'
+ // render(<Button/>)
+ // `,
+ // "/project/node_modules/pkg/entry.js": `export { Button } from './components'`,
+ // "/project/node_modules/pkg/package.json": /* json */ `
+ // {
+ // "main": "./entry.js",
+ // "sideEffects": ["*.css"]
+ // }
+ // `,
+ // "/project/node_modules/pkg/components.jsx": /* jsx */ `
+ // require('./button.css')
+ // export const Button = () => <button/>
+ // `,
+ // "/project/node_modules/pkg/button.css": `button { color: red }`,
+ // },
+ // });
+ // itBundled("dce/TreeShakingJSWithAssociatedCSSExportStarSideEffectsFalse", {
+ // // GENERATED
+ // files: {
+ // "/project/test.jsx": /* jsx */ `
+ // import { Button } from 'pkg'
+ // render(<Button/>)
+ // `,
+ // "/project/node_modules/pkg/entry.js": `export * from './components'`,
+ // "/project/node_modules/pkg/package.json": /* json */ `
+ // {
+ // "main": "./entry.js",
+ // "sideEffects": false
+ // }
+ // `,
+ // "/project/node_modules/pkg/components.jsx": /* jsx */ `
+ // require('./button.css')
+ // export const Button = () => <button/>
+ // `,
+ // "/project/node_modules/pkg/button.css": `button { color: red }`,
+ // },
+ // });
+ // itBundled("dce/TreeShakingJSWithAssociatedCSSExportStarSideEffectsFalseOnlyJS", {
+ // // GENERATED
+ // files: {
+ // "/project/test.jsx": /* jsx */ `
+ // import { Button } from 'pkg'
+ // render(<Button/>)
+ // `,
+ // "/project/node_modules/pkg/entry.js": `export * from './components'`,
+ // "/project/node_modules/pkg/package.json": /* json */ `
+ // {
+ // "main": "./entry.js",
+ // "sideEffects": ["*.css"]
+ // }
+ // `,
+ // "/project/node_modules/pkg/components.jsx": /* jsx */ `
+ // require('./button.css')
+ // export const Button = () => <button/>
+ // `,
+ // "/project/node_modules/pkg/button.css": `button { color: red }`,
+ // },
+ // });
+ // itBundled("dce/TreeShakingJSWithAssociatedCSSUnusedNestedImportSideEffectsFalse", {
+ // // GENERATED
+ // files: {
+ // "/project/test.jsx": /* jsx */ `
+ // import { Button } from 'pkg/button'
+ // render(<Button/>)
+ // `,
+ // "/project/node_modules/pkg/package.json": /* json */ `
+ // {
+ // "sideEffects": false
+ // }
+ // `,
+ // "/project/node_modules/pkg/button.jsx": /* jsx */ `
+ // import styles from './styles'
+ // export const Button = () => <button/>
+ // `,
+ // "/project/node_modules/pkg/styles.js": /* js */ `
+ // import './styles.css'
+ // export default {}
+ // `,
+ // "/project/node_modules/pkg/styles.css": `button { color: red }`,
+ // },
+ // });
+ // itBundled("dce/TreeShakingJSWithAssociatedCSSUnusedNestedImportSideEffectsFalseOnlyJS", {
+ // // GENERATED
+ // files: {
+ // "/project/test.jsx": /* jsx */ `
+ // import { Button } from 'pkg/button'
+ // render(<Button/>)
+ // `,
+ // "/project/node_modules/pkg/package.json": /* json */ `
+ // {
+ // "sideEffects": ["*.css"]
+ // }
+ // `,
+ // "/project/node_modules/pkg/button.jsx": /* jsx */ `
+ // import styles from './styles'
+ // export const Button = () => <button/>
+ // `,
+ // "/project/node_modules/pkg/styles.js": /* js */ `
+ // import './styles.css'
+ // export default {}
+ // `,
+ // "/project/node_modules/pkg/styles.css": `button { color: red }`,
+ // },
+ // });
});
diff --git a/test/bundler/esbuild/default.test.ts b/test/bundler/esbuild/default.test.ts
index 70630fc31..4d0c78c55 100644
--- a/test/bundler/esbuild/default.test.ts
+++ b/test/bundler/esbuild/default.test.ts
@@ -1,6 +1,6 @@
import assert from "assert";
import dedent from "dedent";
-import { bundlerTest, expectBundled, itBundled, testForFile } from "../expectBundled";
+import { ESBUILD_PATH, bundlerTest, expectBundled, itBundled, testForFile } from "../expectBundled";
var { describe, test, expect } = testForFile(import.meta.path);
// Tests ported from:
@@ -3643,9 +3643,8 @@ describe("bundler", () => {
"/entry.js": ['Duplicate injected file "/inject.js"'],
},
});
- return;
+ // TODO: runtime checks for these next two. i think esbuild is doing this one wrong.
itBundled("default/Inject", {
- // GENERATED
files: {
"/entry.js": /* js */ `
let sideEffects = console.log('this should be renamed')
@@ -3664,12 +3663,12 @@ describe("bundler", () => {
export let obj = {}
export let sideEffects = console.log('side effects')
export let noSideEffects = /* @__PURE__ */ console.log('side effects')
- export let injectedAndDefined = 'should not be used'
- let injected_and_defined = 'should not be used'
+ export let injectedAndDefined = 'should not be used FAILED'
+ let injected_and_defined = 'should not be used FAILED'
export { injected_and_defined as 'injected.and.defined' }
`,
"/node_modules/unused/index.js": `console.log('This is unused but still has side effects')`,
- "/node_modules/sideEffects-false/index.js": `console.log('This is unused and has no side effects')`,
+ "/node_modules/sideEffects-false/index.js": `console.log('This is unused and has no side effects. FAILED')`,
"/node_modules/sideEffects-false/package.json": /* json */ `
{
"sideEffects": false
@@ -3684,13 +3683,13 @@ describe("bundler", () => {
}
export { replace2 as 'chain2.prop2' }
`,
- "/collision.js": `export let collide = 123`,
+ "/collision.js": `export let collide = "FAILED"`,
"/re-export.js": /* js */ `
export {re_export} from 'external-pkg'
export {'re.export'} from 'external-pkg2'
`,
},
- format: "cjs",
+ format: "esm",
inject: [
"/inject.js",
"/node_modules/unused/index.js",
@@ -3705,12 +3704,33 @@ describe("bundler", () => {
injectedAndDefined: JSON.stringify("should be used"),
"injected.and.defined": JSON.stringify("should be used"),
},
+ external: ["external-pkg", "external-pkg2"],
+ runtimeFiles: {
+ "/node_modules/external-pkg/index.js": `export let re_export = '1'`,
+ "/node_modules/external-pkg2/index.js": `export let x = '2'; export { x as 're.export' }`,
+ },
+ dce: true,
+ run: {
+ stdout: `
+ side effects
+ This is unused but still has side effects
+ this should be renamed
+ undefined
+ defined
+ should be used
+ should be used
+ [Function: test]
+ [Function: test]
+ 123
+ 1
+ 2
+ `,
+ },
});
itBundled("default/InjectNoBundle", {
- // GENERATED
files: {
"/entry.js": /* js */ `
- let sideEffects = console.log('side effects')
+ let sideEffects = console.log('this should be renamed')
let collide = 123
console.log(obj.prop)
console.log(obj.defined)
@@ -3720,18 +3740,18 @@ describe("bundler", () => {
console.log(chain2.prop2.test)
console.log(collide)
console.log(re_export)
- console.log(reexpo.rt)
+ console.log(re.export)
`,
"/inject.js": /* js */ `
export let obj = {}
- export let sideEffects = console.log('this should be renamed')
+ export let sideEffects = console.log('side effects')
export let noSideEffects = /* @__PURE__ */ console.log('side effects')
- export let injectedAndDefined = 'should not be used'
- let injected_and_defined = 'should not be used'
+ export let injectedAndDefined = 'should not be used FAILED'
+ let injected_and_defined = 'should not be used FAILED'
export { injected_and_defined as 'injected.and.defined' }
`,
"/node_modules/unused/index.js": `console.log('This is unused but still has side effects')`,
- "/node_modules/sideEffects-false/index.js": `console.log('This is unused and has no side effects')`,
+ "/node_modules/sideEffects-false/index.js": `console.log('This is unused and has no side effects. FAILED')`,
"/node_modules/sideEffects-false/package.json": /* json */ `
{
"sideEffects": false
@@ -3741,151 +3761,184 @@ describe("bundler", () => {
export let replace = {
test() {}
}
- let replaceDot = {
+ let replace2 = {
test() {}
}
- export { replaceDot as 'chain2.prop2' }
+ export { replace2 as 'chain2.prop2' }
`,
- "/collision.js": `export let collide = 123`,
+ "/collision.js": `export let collide = "FAILED"`,
"/re-export.js": /* js */ `
export {re_export} from 'external-pkg'
- export {'reexpo.rt'} from 'external-pkg2'
+ export {'re.export'} from 'external-pkg2'
`,
},
- treeShaking: true,
- mode: "passthrough",
+ format: "esm",
+ inject: [
+ "/inject.js",
+ "/node_modules/unused/index.js",
+ "/node_modules/sideEffects-false/index.js",
+ "/replacement.js",
+ "/collision.js",
+ "/re-export.js",
+ ],
define: {
"chain.prop": "replace",
- "obj.defined": '"defined"',
- injectedAndDefined: '"should be used"',
- "injected.and.defined": '"should be used"',
- },
- });
- itBundled("default/InjectJSX", {
- // GENERATED
- files: {
- "/entry.jsx": `console.log(<><div/></>)`,
- "/inject.js": /* js */ `
- export function el() {}
- export function frag() {}
- `,
- },
- define: {
- "React.createElement": "el",
- "React.Fragment": "frag",
- },
- });
- itBundled("default/InjectJSXDotNames", {
- // GENERATED
- files: {
- "/entry.jsx": `console.log(<><div/></>)`,
- "/inject.js": /* js */ `
- function el() {}
- function frag() {}
- export {
- el as 'React.createElement',
- frag as 'React.Fragment',
- }
- `,
- },
- });
- itBundled("default/InjectImportTS", {
- // GENERATED
- files: {
- "/entry.ts": `console.log('here')`,
- "/inject.js": /* js */ `
- // Unused imports are automatically removed in TypeScript files (this
- // is a mis-feature of the TypeScript language). However, injected
- // imports are an esbuild feature so we get to decide what the
- // semantics are. We do not want injected imports to disappear unless
- // they have been explicitly marked as having no side effects.
- console.log('must be present')
- `,
- },
- format: "esm",
- mode: "convertformat",
- });
- itBundled("default/InjectImportOrder", {
- // GENERATED
- files: {
- "/entry.ts": /* ts */ `
- import 'third'
- console.log('third')
- `,
- "/inject-1.js": /* js */ `
- import 'first'
- console.log('first')
- `,
- "/inject-2.js": /* js */ `
- import 'second'
- console.log('second')
- `,
- },
- inject: ["/inject-1.js", "/inject-2.js"],
- });
- itBundled("default/InjectAssign", {
- // GENERATED
- files: {
- "/entry.js": /* js */ `
- test = true
- foo.bar = true
- defined = true
- `,
- "/inject.js": /* js */ `
- export let test = 0
- let fooBar = 1
- let someDefine = 2
- export { fooBar as 'foo.bar' }
- export { someDefine as 'some.define' }
- `,
+ "obj.defined": JSON.stringify("defined"),
+ injectedAndDefined: JSON.stringify("should be used"),
+ "injected.and.defined": JSON.stringify("should be used"),
},
- inject: ["/inject.js"],
- define: {
- defined: "some.define",
+ runtimeFiles: {
+ "/node_modules/external-pkg/index.js": `export let re_export = '1'`,
+ "/node_modules/external-pkg2/index.js": `export let x = '2'; export { x as 're.export' }`,
},
- });
- itBundled("default/InjectWithDefine", {
- files: {
- "/entry.js": /* js */ `
- console.log(
- // define wins over inject
- both === 'define',
- bo.th === 'defi.ne',
- // define forwards to inject
- first === 'success (identifier)',
- fir.st === 'success (dot name)',
- )
- `,
- "/inject.js": /* js */ `
- export let both = 'inject'
- export let first = 'TEST FAILED!'
- export let second = 'success (identifier)'
-
- let both2 = 'inject'
- let first2 = 'TEST FAILED!'
- let second2 = 'success (dot name)'
- export {
- both2 as 'bo.th',
- first2 as 'fir.st',
- second2 as 'seco.nd',
- }
+ dce: true,
+ treeShaking: true,
+ mode: "transform",
+ run: {
+ stdout: `
+ side effects
+ This is unused but still has side effects
+ this should be renamed
+ undefined
+ defined
+ should be used
+ should be used
+ [Function: test]
+ [Function: test]
+ 123
+ 1
+ 2
`,
},
- inject: ["/inject.js"],
- define: {
- "both": '"define"',
- "bo.th": '"defi.ne"',
- "first": "second",
- "fir.st": "seco.nd",
- },
});
+ // itBundled("default/InjectJSX", {
+ // files: {
+ // "/entry.jsx": `console.log(<><div/></>)`,
+ // "/inject.js": /* js */ `
+ // export function el() {}
+ // export function frag() {}
+ // `,
+ // },
+ // define: {
+ // "React.createElement": "el",
+ // "React.Fragment": "frag",
+ // },
+ // inject: ["/inject.js"],
+ // });
+ // itBundled("default/InjectJSXDotNames", {
+ // // GENERATED
+ // files: {
+ // "/entry.jsx": `console.log(<><div/></>)`,
+ // "/inject.js": /* js */ `
+ // function el() {}
+ // function frag() {}
+ // export {
+ // el as 'React.createElement',
+ // frag as 'React.Fragment',
+ // }
+ // `,
+ // },
+ // });
+ // itBundled("default/InjectImportTS", {
+ // // GENERATED
+ // files: {
+ // "/entry.ts": `console.log('here')`,
+ // "/inject.js": /* js */ `
+ // // Unused imports are automatically removed in TypeScript files (this
+ // // is a mis-feature of the TypeScript language). However, injected
+ // // imports are an esbuild feature so we get to decide what the
+ // // semantics are. We do not want injected imports to disappear unless
+ // // they have been explicitly marked as having no side effects.
+ // console.log('must be present')
+ // `,
+ // },
+ // format: "esm",
+ // });
+ // itBundled("default/InjectImportOrder", {
+ // // GENERATED
+ // files: {
+ // "/entry.ts": /* ts */ `
+ // import 'third'
+ // console.log('third')
+ // `,
+ // "/inject-1.js": /* js */ `
+ // import 'first'
+ // console.log('first')
+ // `,
+ // "/inject-2.js": /* js */ `
+ // import 'second'
+ // console.log('second')
+ // `,
+ // },
+ // inject: ["/inject-1.js", "/inject-2.js"],
+ // });
+ // itBundled("default/InjectAssign", {
+ // // GENERATED
+ // files: {
+ // "/entry.js": /* js */ `
+ // test = true
+ // foo.bar = true
+ // defined = true
+ // `,
+ // "/inject.js": /* js */ `
+ // export let test = 0
+ // let fooBar = 1
+ // let someDefine = 2
+ // export { fooBar as 'foo.bar' }
+ // export { someDefine as 'some.define' }
+ // `,
+ // },
+ // inject: ["/inject.js"],
+ // define: {
+ // defined: "some.define",
+ // },
+ // });
+ // itBundled("default/InjectWithDefine", {
+ // files: {
+ // "/entry.js": /* js */ `
+ // console.log(
+ // // define wins over inject
+ // both === 'define',
+ // bo.th === 'defi.ne',
+ // // define forwards to inject
+ // first === 'success (identifier)',
+ // fir.st === 'success (dot name)',
+ // )
+ // `,
+ // "/inject.js": /* js */ `
+ // export let both = 'inject'
+ // export let first = 'TEST FAILED!'
+ // export let second = 'success (identifier)'
+
+ // let both2 = 'inject'
+ // let first2 = 'TEST FAILED!'
+ // let second2 = 'success (dot name)'
+ // export {
+ // both2 as 'bo.th',
+ // first2 as 'fir.st',
+ // second2 as 'seco.nd',
+ // }
+ // `,
+ // },
+ // inject: ["/inject.js"],
+ // define: {
+ // "both": '"define"',
+ // "bo.th": '"defi.ne"',
+ // "first": "second",
+ // "fir.st": "seco.nd",
+ // },
+ // });
itBundled("default/Outbase", {
- // GENERATED
files: {
"/a/b/c.js": `console.log('c')`,
"/a/b/d.js": `console.log('d')`,
},
entryPoints: ["/a/b/c.js", "/a/b/d.js"],
+ outbase: "/",
+ onAfterBundle(api) {
+ api.assertFileExists("/out/a/b/c.js");
+ api.assertFileExists("/out/a/b/d.js");
+ },
});
itBundled("default/AvoidTDZ", {
// GENERATED
@@ -3895,14 +3948,23 @@ describe("bundler", () => {
static foo = new Foo
}
let foo = Foo.foo
- console.log(foo)
+ console.log(JSON.stringify(foo))
export class Bar {}
export let bar = 123
`,
},
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ import * as mod from './out';
+ console.log(JSON.stringify(mod));
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: '{}\n{"bar":123}',
+ },
});
itBundled("default/AvoidTDZNoBundle", {
- // GENERATED
files: {
"/entry.js": /* js */ `
class Foo {
@@ -3914,7 +3976,17 @@ describe("bundler", () => {
export let bar = 123
`,
},
- mode: "passthrough",
+ mode: "transform",
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ import * as mod from './out';
+ console.log(JSON.stringify(mod));
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: '{}\n{"bar":123}',
+ },
});
itBundled("default/DefineImportMeta", {
files: {
@@ -3943,7 +4015,6 @@ describe("bundler", () => {
},
});
itBundled("default/DefineImportMetaES5", {
- // GENERATED
files: {
"/replaced.js": `console.log(import.meta.x)`,
"/kept.js": `console.log(import.meta.y)`,
@@ -3953,41 +4024,45 @@ describe("bundler", () => {
define: {
"import.meta.x": 1,
},
- /* TODO FIX expectedScanLog: `dead-code.js: WARNING: "import.meta" is not available in the configured target environment and will be empty
- kept.js: WARNING: "import.meta" is not available in the configured target environment and will be empty
- `, */
- });
- itBundled("default/InjectImportMeta", {
- // GENERATED
- files: {
- "/entry.js": /* js */ `
- console.log(
- // These should be fully substituted
- import.meta,
- import.meta.foo,
- import.meta.foo.bar,
-
- // Should just substitute "import.meta.foo"
- import.meta.foo.baz,
-
- // This should not be substituted
- import.meta.bar,
- )
- `,
- "/inject.js": /* js */ `
- let foo = 1
- let bar = 2
- let baz = 3
- export {
- foo as 'import.meta',
- bar as 'import.meta.foo',
- baz as 'import.meta.foo.bar',
- }
- `,
+ unsupportedJSFeatures: ["import-meta"],
+ run: [
+ { file: "/out/replaced.js", stdout: "1" },
+ { file: "/out/kept.js", stdout: "undefined" },
+ ],
+ onAfterBundle(api) {
+ api.expectFile("/out/dead-code.js").toBe("");
},
});
+ // itBundled("default/InjectImportMeta", {
+ // // GENERATED
+ // files: {
+ // "/entry.js": /* js */ `
+ // console.log(
+ // // These should be fully substituted
+ // import.meta,
+ // import.meta.foo,
+ // import.meta.foo.bar,
+
+ // // Should just substitute "import.meta.foo"
+ // import.meta.foo.baz,
+
+ // // This should not be substituted
+ // import.meta.bar,
+ // )
+ // `,
+ // "/inject.js": /* js */ `
+ // let foo = 1
+ // let bar = 2
+ // let baz = 3
+ // export {
+ // foo as 'import.meta',
+ // bar as 'import.meta.foo',
+ // baz as 'import.meta.foo.bar',
+ // }
+ // `,
+ // },
+ // });
itBundled("default/DefineThis", {
- // GENERATED
files: {
"/entry.js": /* js */ `
ok(
@@ -4015,7 +4090,7 @@ describe("bundler", () => {
})();
// Nothing should be substituted in this code
- (function() {
+ export default function() {
doNotSubstitute(
this,
this.foo,
@@ -4023,20 +4098,52 @@ describe("bundler", () => {
this.foo.baz,
this.bar,
);
- })();
+ };
`,
},
define: {
- this: 1,
- "this.foo": 2,
- "this.foo.bar": 3,
+ this: "_replaced",
+ "this.foo": "_replaced_foo",
+ "this.foo.bar": "_replaced_foo_bar",
+ },
+ onAfterBundle(api) {
+ const split = api.readFile("/out.js").split("doNotSubstitute");
+ expect(split.length).toBe(2);
+ assert(!split[0].includes("this"), "this should not be substituted in the first two cases");
+ assert([...split[1].matchAll(/this/g)].length === 5, "there should be 5 mentions of this in the third case");
+ },
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ globalThis.ok = (...args) => console.log(JSON.stringify(args));
+ globalThis.doNotSubstitute = (...args) => console.log(JSON.stringify(args));
+ globalThis._replaced = { foo: 1 };
+ globalThis._replaced_foo = { baz: 2 };
+ globalThis._replaced_foo_bar = 3;
+ const { default: fn } = await import('./out.js');
+
+ fn.call({
+ foo: {
+ bar: 4,
+ baz: 5,
+ },
+ bar: 6,
+ });
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: `
+ [{"foo":1},{"baz":2},3,2,null]
+ [{"foo":1},{"baz":2},3,2,null]
+ [{"foo":{"bar":4,"baz":5},"bar":6},{"bar":4,"baz":5},4,5,6]
+ `,
},
});
itBundled("default/DefineOptionalChain", {
// GENERATED
files: {
"/entry.js": /* js */ `
- console.log([
+ log([
a.b.c,
a?.b.c,
a.b?.c,
@@ -4054,12 +4161,24 @@ describe("bundler", () => {
define: {
"a.b.c": 1,
},
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ globalThis.log = (...args) => console.log(JSON.stringify(args));
+ globalThis.a = { B: { C: 2 } };
+ globalThis.b = "B";
+ globalThis.c = "C";
+ await import('./out.js');
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: `[[1,1,1],[1,1,1],[2,2,2]]`,
+ },
});
itBundled("default/DefineOptionalChainLowered", {
- // GENERATED
files: {
"/entry.js": /* js */ `
- console.log([
+ log([
a.b.c,
a?.b.c,
a.b?.c,
@@ -4071,15 +4190,36 @@ describe("bundler", () => {
a[b][c],
a?.[b][c],
a[b]?.[c],
+ a?.[d][c],
+ a[d]?.[e],
])
`,
},
+ unsupportedJSFeatures: ["optional-chain"],
define: {
"a.b.c": 1,
},
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ globalThis.log = (...args) => console.log(JSON.stringify(args));
+ globalThis.a = { B: { C: 2 }, D: { } };
+ globalThis.b = "B";
+ globalThis.c = "C";
+ globalThis.d = "D";
+ globalThis.e = "E";
+ await import('./out.js');
+ `,
+ },
+ onAfterBundle(api) {
+ const code = api.readFile("/out.js");
+ assert(!code.includes("?."), "code should not contain optional chaining");
+ },
+ run: {
+ file: "/test.js",
+ stdout: `[[1,1,1],[1,1,1],[2,2,2,null,null]]`,
+ },
});
itBundled("default/DefineInfiniteLoopIssue2407", {
- // GENERATED
files: {
"/entry.js": /* js */ `
a.b()
@@ -4092,88 +4232,143 @@ describe("bundler", () => {
"c.a": "a.b",
"x.y": "y",
},
- });
- itBundled("default/DefineAssignWarning", {
- // GENERATED
- files: {
- "/read.js": /* js */ `
- console.log(
- [a, b.c, b['c']],
- [d, e.f, e['f']],
- [g, h.i, h['i']],
- )
- `,
- "/write.js": /* js */ `
- console.log(
- [a = 0, b.c = 0, b['c'] = 0],
- [d = 0, e.f = 0, e['f'] = 0],
- [g = 0, h.i = 0, h['i'] = 0],
- )
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ globalThis.b = { c: () => console.log('1') };
+ globalThis.y = () => console.log('2');
+ await import('./out.js');
`,
},
- entryPoints: ["/read.js", "/write.js"],
- define: {
- a: "null",
- "b.c": "null",
- d: "ident",
- "e.f": "ident",
- g: "dot.chain",
- "h.i": "dot.chain",
+ run: {
+ file: "/test.js",
+ stdout: "1\n2",
},
});
+ // TODO: this doesnt warn in esbuild ???
+ // itBundled("default/DefineAssignWarning", {
+ // // GENERATED
+ // files: {
+ // "/read.js": /* js */ `
+ // console.log(
+ // [a, b.c, b['c']],
+ // [d, e.f, e['f']],
+ // [g, h.i, h['i']],
+ // )
+ // `,
+ // "/write.js": /* js */ `
+ // console.log(
+ // [a = 0, b.c = 0, b['c'] = 0],
+ // [d = 0, e.f = 0, e['f'] = 0],
+ // [g = 0, h.i = 0, h['i'] = 0],
+ // )
+ // `,
+ // },
+ // entryPoints: ["/read.js", "/write.js"],
+ // define: {
+ // a: "null",
+ // "b.c": "null",
+ // d: "ident",
+ // "e.f": "ident",
+ // g: "dot.chain",
+ // "h.i": "dot.chain",
+ // },
+ // });
itBundled("default/KeepNamesTreeShaking", {
- // GENERATED
files: {
"/entry.js": /* js */ `
- function fnStmtRemove() {}
- function fnStmtKeep() {}
- x = fnStmtKeep
-
- let fnExprRemove = function remove() {}
- let fnExprKeep = function keep() {}
- x = fnExprKeep
-
- class clsStmtRemove {}
- class clsStmtKeep {}
- new clsStmtKeep()
-
- let clsExprRemove = class remove {}
- let clsExprKeep = class keep {}
- new clsExprKeep()
+ (function() {
+ function fnStmtRemove() {}
+ function fnStmtKeep() {}
+ x = fnStmtKeep
+
+ let fnExprRemove = function remove() {}
+ let fnExprKeep = function keepFn() {}
+ x = fnExprKeep
+
+ class clsStmtRemove {}
+ class clsStmtKeep {}
+ new clsStmtKeep()
+
+ let clsExprRemove = class remove {}
+ let clsExprKeep = class keepClass {}
+ new clsExprKeep()
+ })();
`,
},
keepNames: true,
+ dce: true,
+ onAfterBundle(api) {
+ // to properly check that keep names actually worked, we need to minify the
+ // file and THEN check for the names. we do this separatly just so that we know that
+ // the bundler's minifier doesn't mess anything up.
+ Bun.spawnSync([ESBUILD_PATH, "--minify-identifiers", "--outfile=out.min.js", "out.js"], { cwd: api.root });
+ const code = api.readFile("/out.min.js");
+ const checks = ["fnStmtKeep", "keepFn", "clsStmtKeep", "keepClass"];
+ for (const check of checks) {
+ assert(code.includes(check), `code should contain ${check} past minifying`);
+ }
+ },
});
itBundled("default/KeepNamesClassStaticName", {
- // GENERATED
files: {
"/entry.js": /* js */ `
- class A { static foo }
- class B { static name }
- class C { static name() {} }
- class D { static get name() {} }
- class E { static set name(x) {} }
- class F { static ['name'] = 0 }
+ class ClassName1A { static foo = 1 }
+ class ClassName1B { static name = 2 }
+ class ClassName1C { static name() {} }
+ class ClassName1D { static get name() {} }
+ class ClassName1E { static set name(x) {} }
+ class ClassName1F { static ['name'] = 0 }
- let a = class a { static foo }
- let b = class b { static name }
- let c = class c { static name() {} }
- let d = class d { static get name() {} }
- let e = class e { static set name(x) {} }
- let f = class f { static ['name'] = 0 }
+ let a = class ClassName2a { static foo }
+ let b = class ClassName2b { static name }
+ let c = class ClassName2c { static name() {} }
+ let d = class ClassName2d { static get name() {} }
+ let e = class ClassName2e { static set name(x) {} }
+ let f = class ClassName2f { static ['name'] = 0 }
- let a2 = class { static foo }
- let b2 = class { static name }
- let c2 = class { static name() {} }
- let d2 = class { static get name() {} }
- let e2 = class { static set name(x) {} }
- let f2 = class { static ['name'] = 0 }
+ let ClassName_a2 = class { static foo }
+ let ClassName_b2 = class { static name }
+ let ClassName_c2 = class { static name() {} }
+ let ClassName_d2 = class { static get name() {} }
+ let ClassName_e2 = class { static set name(x) {} }
+ let ClassName_f2 = class { static ['name'] = 0 }
+
+ export { ClassName1A, ClassName1B, ClassName1C, ClassName1D, ClassName1E, ClassName1F, a, b, c, d, e, f, ClassName_a2 as a2, ClassName_b2 as b2,ClassName_c2 as c2,ClassName_d2 as d2,ClassName_e2 as e2,ClassName_f2 as f2 }
`,
},
- mode: "passthrough",
+ keepNames: true,
+ onAfterBundle(api) {
+ // to properly check that keep names actually worked, we need to minify the
+ // file and THEN check for the names. we do this separatly just so that we know that
+ // the bundler's minifier doesn't mess anything up.
+ Bun.spawnSync([ESBUILD_PATH, "--minify-identifiers", "--outfile=out.min.js", "out.js"], { cwd: api.root });
+ const code = api.readFile("/out.min.js");
+ const checks = [
+ "ClassName1A",
+ "ClassName1B",
+ "ClassName1C",
+ "ClassName1D",
+ "ClassName1E",
+ "ClassName1F",
+ "ClassName2a",
+ "ClassName2b",
+ "ClassName2c",
+ "ClassName2d",
+ "ClassName2e",
+ "ClassName2f",
+ "ClassName_a2",
+ "ClassName_b2",
+ "ClassName_c2",
+ "ClassName_d2",
+ "ClassName_e2",
+ "ClassName_f2",
+ ];
+ for (const check of checks) {
+ assert(code.includes(check), `code should contain ${check} past minifying`);
+ }
+ },
});
itBundled("default/CharFreqIgnoreComments", {
- // GENERATED
files: {
"/a.js": /* js */ `
export default function(one, two, three, four) {
@@ -4193,32 +4388,39 @@ describe("bundler", () => {
// LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
`,
},
+ minifyIdentifiers: true,
entryPoints: ["/a.js", "/b.js"],
+ onAfterBundle(api) {
+ function capture(str: string) {
+ return str.match(/function.*?\(\s*(\w+),\s*(\w+),\s*(\w+),\s*(\w+)\)/)!.slice(1);
+ }
+ const a = capture(api.readFile("/out/a.js"));
+ const b = capture(api.readFile("/out/b.js"));
+ expect(a).toEqual(b);
+ expect(a).not.toEqual(["one", "two", "three", "four"]);
+ },
});
itBundled("default/ImportRelativeAsPackage", {
- // GENERATED
files: {
"/Users/user/project/src/entry.js": `import 'some/other/file'`,
"/Users/user/project/src/some/other/file.js": ``,
},
- /* TODO FIX expectedScanLog: `Users/user/project/src/entry.js: ERROR: Could not resolve "some/other/file"
- NOTE: Use the relative path "./some/other/file" to reference the file "Users/user/project/src/some/other/file.js". Without the leading "./", the path "some/other/file" is being interpreted as a package path instead.
- `, */
+ bundleErrors: {
+ "/Users/user/project/src/entry.js": [`Could not resolve: "some/other/file". Maybe you need to "bun install"?`],
+ },
});
itBundled("default/ForbidConstAssignWhenBundling", {
- // GENERATED
files: {
"/entry.js": /* js */ `
const x = 1
x = 2
`,
},
- /* TODO FIX expectedScanLog: `entry.js: ERROR: Cannot assign to "x" because it is a constant
- entry.js: NOTE: The symbol "x" was declared a constant here:
- `, */
+ bundleErrors: {
+ "/entry.js": [`Cannot assign to "x" because it is a constant`],
+ },
});
itBundled("default/ConstWithLet", {
- // GENERATED
files: {
"/entry.js": /* js */ `
const a = 1; console.log(a)
@@ -4229,7 +4431,14 @@ describe("bundler", () => {
for (const e of x) console.log(e)
`,
},
+ minifySyntax: true,
+ onAfterBundle(api) {
+ const code = api.readFile("/out.js");
+ expect(code).not.toContain("const");
+ assert([...code.matchAll(/let/g)].length === 3, "should have 3 let statements");
+ },
});
+ // TODO: this fails on esbuild ???
itBundled("default/ConstWithLetNoBundle", {
// GENERATED
files: {
@@ -4242,41 +4451,36 @@ describe("bundler", () => {
for (const e of x) console.log(e)
`,
},
- mode: "passthrough",
- });
- itBundled("default/ConstWithLetNoMangle", {
- // GENERATED
- files: {
- "/entry.js": /* js */ `
- const a = 1; console.log(a)
- if (true) { const b = 2; console.log(b) }
- for (const c = x;;) console.log(c)
- for (const d in x) console.log(d)
- for (const e of x) console.log(e)
- `,
- },
- });
- itBundled("default/RequireMainCacheCommonJS", {
- // GENERATED
- files: {
- "/entry.js": /* js */ `
- console.log('is main:', require.main === module)
- console.log(require('./is-main'))
- console.log('cache:', require.cache);
- `,
- "/is-main.js": `module.exports = require.main === module`,
+ minifySyntax: true,
+ mode: "transform",
+ onAfterBundle(api) {
+ const code = api.readFile("/out.js");
+ expect(code).not.toContain("const");
+ assert([...code.matchAll(/let/g)].length === 3, "should have 3 let statements");
},
- platform: "node",
});
+ // TODO: this is hard to test since bun runtime doesn't support require.main and require.cache
+ // i'm not even sure what we want our behavior to be for this case.
+ // itBundled("default/RequireMainCacheCommonJS", {
+ // files: {
+ // "/entry.js": /* js */ `
+ // console.log('is main:', require.main === module)
+ // console.log(require('./is-main'))
+ // console.log('cache:', require.cache);
+ // `,
+ // "/is-main.js": `module.exports = require.main === module`,
+ // },
+ // format: "cjs",
+ // platform: "node",
+ // });
itBundled("default/ExternalES6ConvertedToCommonJS", {
- // GENERATED
files: {
"/entry.js": /* js */ `
- require('./a')
- require('./b')
- require('./c')
- require('./d')
- require('./e')
+ console.log(JSON.stringify(require('./a')));
+ console.log(JSON.stringify(require('./b')));
+ console.log(JSON.stringify(require('./c')));
+ console.log(JSON.stringify(require('./d')));
+ console.log(JSON.stringify(require('./e')));
`,
"/a.js": /* js */ `
import * as ns from 'x'
@@ -4290,88 +4494,87 @@ describe("bundler", () => {
"/d.js": `export {ns} from 'x'`,
"/e.js": `export * from 'x'`,
},
+ external: ["x"],
format: "esm",
+ runtimeFiles: {
+ "/node_modules/x/index.js": /* js */ `
+ export const ns = 123
+ export const ns2 = 456
+ `,
+ },
+ run: {
+ stdout: `
+ {"ns":{"ns":123,"ns2":456}}
+ {"ns":{"ns":123,"ns2":456}}
+ {"ns":{"ns":123,"ns2":456}}
+ {"ns":123}
+ {"ns":123,"ns2":456}
+ `,
+ },
});
- itBundled("default/CallImportNamespaceWarning", {
- // GENERATED
- files: {
- "/js.js": /* js */ `
- import * as a from "a"
- import {b} from "b"
- import c from "c"
- a()
- b()
- c()
- new a()
- new b()
- new c()
- `,
- "/ts.ts": /* ts */ `
- import * as a from "a"
- import {b} from "b"
- import c from "c"
- a()
- b()
- c()
- new a()
- new b()
- new c()
- `,
- "/jsx-components.jsx": /* jsx */ `
- import * as A from "a"
- import {B} from "b"
- import C from "c"
- <A/>;
- <B/>;
- <C/>;
- `,
- "/jsx-a.jsx": /* jsx */ `
- // @jsx a
- import * as a from "a"
- <div/>
- `,
- "/jsx-b.jsx": /* jsx */ `
- // @jsx b
- import {b} from "b"
- <div/>
- `,
- "/jsx-c.jsx": /* jsx */ `
- // @jsx c
- import c from "c"
- <div/>
- `,
- },
- entryPoints: ["/js.js", "/ts.ts", "/jsx-components.jsx", "/jsx-a.jsx", "/jsx-b.jsx", "/jsx-c.jsx"],
- mode: "convertformat",
- /* TODO FIX expectedScanLog: `js.js: WARNING: Calling "a" will crash at run-time because it's an import namespace object, not a function
- js.js: NOTE: Consider changing "a" to a default import instead:
- js.js: WARNING: Constructing "a" will crash at run-time because it's an import namespace object, not a constructor
- js.js: NOTE: Consider changing "a" to a default import instead:
- jsx-a.jsx: WARNING: Calling "a" will crash at run-time because it's an import namespace object, not a function
- jsx-a.jsx: NOTE: Consider changing "a" to a default import instead:
- jsx-components.jsx: WARNING: Using "A" in a JSX expression will crash at run-time because it's an import namespace object, not a component
- jsx-components.jsx: NOTE: Consider changing "A" to a default import instead:
- ts.ts: WARNING: Calling "a" will crash at run-time because it's an import namespace object, not a function
- ts.ts: NOTE: Consider changing "a" to a default import instead:
- NOTE: Make sure to enable TypeScript's "esModuleInterop" setting so that TypeScript's type checker generates an error when you try to do this. You can read more about this setting here: https://www.typescriptlang.org/tsconfig#esModuleInterop
- ts.ts: WARNING: Constructing "a" will crash at run-time because it's an import namespace object, not a constructor
- ts.ts: NOTE: Consider changing "a" to a default import instead:
- NOTE: Make sure to enable TypeScript's "esModuleInterop" setting so that TypeScript's type checker generates an error when you try to do this. You can read more about this setting here: https://www.typescriptlang.org/tsconfig#esModuleInterop
- `, */
- });
+ // TODO:
+ // itBundled("default/CallImportNamespaceWarning", {
+ // files: {
+ // "/js.js": /* js */ `
+ // import * as a from "a"
+ // import {b} from "b"
+ // import c from "c"
+ // a()
+ // b()
+ // c()
+ // new a()
+ // new b()
+ // new c()
+ // `,
+ // "/ts.ts": /* ts */ `
+ // import * as a from "a"
+ // import {b} from "b"
+ // import c from "c"
+ // a()
+ // b()
+ // c()
+ // new a()
+ // new b()
+ // new c()
+ // `,
+ // "/jsx-components.jsx": /* jsx */ `
+ // import * as A from "a"
+ // import {B} from "b"
+ // import C from "c"
+ // <A/>;
+ // <B/>;
+ // <C/>;
+ // `,
+ // "/jsx-a.jsx": /* jsx */ `
+ // // @jsx a
+ // import * as a from "a"
+ // <div/>
+ // `,
+ // "/jsx-b.jsx": /* jsx */ `
+ // // @jsx b
+ // import {b} from "b"
+ // <div/>
+ // `,
+ // "/jsx-c.jsx": /* jsx */ `
+ // // @jsx c
+ // import c from "c"
+ // <div/>
+ // `,
+ // },
+ // entryPoints: ["/js.js", "/ts.ts", "/jsx-components.jsx", "/jsx-a.jsx", "/jsx-b.jsx", "/jsx-c.jsx"],
+ // mode: "transform",
+ // external: ["a", "b", "c", "react/jsx-dev-runtime"],
+ // });
+ return;
+ // I cant get bun to use `this` as the JSX runtime. It's a pretty silly idea anyways.
itBundled("default/JSXThisValueCommonJS", {
- // GENERATED
files: {
"/factory.jsx": /* jsx */ `
- console.log([
- <x />,
- /* @__PURE__ */ this('x', null),
- ])
+ CHECK1(<x />);
+ CHECK1(/* @__PURE__ */ this('x', null));
f = function() {
- console.log([
- <y />,
- /* @__PURE__ */ this('y', null),
- ])
+ CHECK2(<y />);
+ CHECK2(/* @__PURE__ */ this('y', null));
}
`,
"/fragment.jsx": /* jsx */ `
@@ -4388,7 +4591,10 @@ describe("bundler", () => {
`,
},
entryPoints: ["/factory.jsx", "/fragment.jsx"],
+ external: ["react/jsx-dev-runtime", "react"],
jsx: {
+ development: false,
+ automaticRuntime: false,
factory: "this",
fragment: "this",
},
diff --git a/test/bundler/expectBundled.md b/test/bundler/expectBundled.md
index f8883459d..4e944543c 100644
--- a/test/bundler/expectBundled.md
+++ b/test/bundler/expectBundled.md
@@ -182,3 +182,9 @@ itBundled("importstar/ReExportStarExternalIIFE", {
## dce: true
This parameter checks the bundle for strings like `DROP`, `REMOVE`, and `FAIL` within the bundle, and will throw an error. This is handy for dead code elimination tests where you can just name variables that should be removed with one of those trigger words. In addition, `KEEP`, `PRESERVE`, and `KEEPME` is scanned in the source code and will throw an error if the count of those strings is not equal to the count of the corresponding trigger strings.
+
+Places that are not required to be dce'd contain `POSSIBLE_REMOVAL` and do not trigger an error if not removed. These might be able to be optimized in the future.
+
+## 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`).
diff --git a/test/bundler/expectBundled.ts b/test/bundler/expectBundled.ts
index 87e31b447..cd62ce515 100644
--- a/test/bundler/expectBundled.ts
+++ b/test/bundler/expectBundled.ts
@@ -42,6 +42,8 @@ if (ESBUILD) {
console.warn("NOTE: using esbuild for bun build tests");
}
+export const ESBUILD_PATH = import.meta.resolveSync("esbuild/bin/esbuild");
+
export interface BundlerTestInput {
// file options
files: Record<string, string>;
@@ -131,6 +133,11 @@ export interface BundlerTestInput {
*/
dce?: boolean;
/**
+ * Override the number of keep markers, which is auto detected by default.
+ * Does nothing if dce is false.
+ */
+ dceKeepMarkerCount?: number | Record<string, number> | false;
+ /**
* Shorthand for testing splitting cases. Given a list of files, checks that each file doesn't
* contain the specified strings. This lets us test that certain values are not bundled.
*/
@@ -192,6 +199,7 @@ export function expectBundled(id: string, opts: BundlerTestInput, dryRun?: boole
bundleErrors,
bundleWarnings,
dce,
+ dceKeepMarkerCount,
define,
entryNames,
entryPoints,
@@ -202,13 +210,17 @@ export function expectBundled(id: string, opts: BundlerTestInput, dryRun?: boole
globalName,
inject,
jsx = {},
+ keepNames,
legalComments,
+ loader,
+ mainFields,
metafile,
minifyIdentifiers,
minifySyntax,
minifyWhitespace,
mode,
onAfterBundle,
+ outbase,
outdir,
outfile,
outputPaths,
@@ -218,6 +230,7 @@ export function expectBundled(id: string, opts: BundlerTestInput, dryRun?: boole
skipOnEsbuild,
sourceMap,
splitting,
+ treeShaking,
unsupportedCSSFeatures,
unsupportedJSFeatures,
...unknownProps
@@ -244,9 +257,6 @@ export function expectBundled(id: string, opts: BundlerTestInput, dryRun?: boole
if (bundleWarnings === true) bundleWarnings = {};
const useOutFile = outfile ? true : outdir ? false : entryPoints.length === 1;
- if (!ESBUILD && jsx.automaticRuntime) {
- throw new Error("jsx.automaticRuntime not implemented in bun build");
- }
if (!ESBUILD && format !== "esm") {
throw new Error("formats besides esm not implemented in bun build");
}
@@ -262,6 +272,21 @@ export function expectBundled(id: string, opts: BundlerTestInput, dryRun?: boole
if (!ESBUILD && unsupportedCSSFeatures && unsupportedCSSFeatures.length) {
throw new Error("unsupportedCSSFeatures not implemented in bun build");
}
+ if (!ESBUILD && outbase) {
+ throw new Error("outbase not implemented in bun build");
+ }
+ if (!ESBUILD && keepNames) {
+ throw new Error("keepNames not implemented in bun build");
+ }
+ if (!ESBUILD && minifyIdentifiers) {
+ throw new Error("minifyIdentifiers not implemented in bun build");
+ }
+ if (!ESBUILD && mainFields) {
+ throw new Error("mainFields not implemented in bun build");
+ }
+ if (!ESBUILD && loader) {
+ throw new Error("loader not implemented in bun build");
+ }
if (ESBUILD && skipOnEsbuild) {
return;
}
@@ -281,9 +306,11 @@ export function expectBundled(id: string, opts: BundlerTestInput, dryRun?: boole
outfile = useOutFile ? path.join(root, outfile ?? "/out.js") : undefined;
outdir = !useOutFile ? path.join(root, outdir ?? "/out") : undefined;
metafile = metafile ? path.join(root, metafile) : undefined;
- outputPaths = outputPaths
- ? outputPaths.map(file => path.join(root, file))
- : entryPaths.map(file => path.join(outdir!, path.basename(file)));
+ outputPaths = (
+ outputPaths
+ ? outputPaths.map(file => path.join(root, file))
+ : entryPaths.map(file => path.join(outdir!, path.basename(file)))
+ ).map(x => x.replace(/\.ts$/, ".js"));
if (outdir) {
entryNames ??= "[name].[ext]";
@@ -316,25 +343,30 @@ export function expectBundled(id: string, opts: BundlerTestInput, dryRun?: boole
outfile ? `--outfile=${outfile}` : `--outdir=${outdir}`,
define && Object.entries(define).map(([k, v]) => ["--define", `${k}=${v}`]),
`--platform=${platform}`,
+ external && external.map(x => ["--external", x]),
minifyIdentifiers && `--minify-identifiers`,
minifySyntax && `--minify-syntax`,
minifyWhitespace && `--minify-whitespace`,
globalName && `--global-name=${globalName}`,
- external && external.map(x => ["--external", x]),
- inject && inject.map(x => ["--inject", path.join(root, x)]),
- jsx.automaticRuntime && "--jsx=automatic",
+ // inject && inject.map(x => ["--inject", path.join(root, x)]),
+ jsx.automaticRuntime === false && "--jsx=classic",
jsx.factory && `--jsx-factory=${jsx.factory}`,
jsx.fragment && `--jsx-fragment=${jsx.fragment}`,
- jsx.development && `--jsx-dev`,
+ jsx.development === false && `--jsx-production`,
// metafile && `--metafile=${metafile}`,
// sourceMap && `--sourcemap${sourceMap !== true ? `=${sourceMap}` : ""}`,
entryNames && entryNames !== "[name].[ext]" && [`--entry-names`, entryNames],
// `--format=${format}`,
// legalComments && `--legal-comments=${legalComments}`,
splitting && `--splitting`,
+ // treeShaking && `--tree-shaking`,
+ // outbase && `--outbase=${outbase}`,
+ // keepNames && `--keep-names`,
+ // mainFields && `--main-fields=${mainFields}`,
+ // loader && Object.entries(loader).map(([k, v]) => ["--loader", `${k}=${v}`]),
]
: [
- Bun.which("esbuild"),
+ ESBUILD_PATH,
mode === "bundle" && "--bundle",
outfile ? `--outfile=${outfile}` : `--outdir=${outdir}`,
`--format=${format}`,
@@ -356,6 +388,11 @@ export function expectBundled(id: string, opts: BundlerTestInput, dryRun?: boole
banner && `--banner:js=${banner}`,
legalComments && `--legal-comments=${legalComments}`,
splitting && `--splitting`,
+ treeShaking && `--tree-shaking`,
+ outbase && `--outbase=${path.join(root, outbase)}`,
+ keepNames && `--keep-names`,
+ mainFields && `--main-fields=${mainFields.join(",")}`,
+ loader && Object.entries(loader).map(([k, v]) => `--loader:${k}=${v}`),
[...(unsupportedJSFeatures ?? []), ...(unsupportedCSSFeatures ?? [])].map(x => `--supported:${x}=false`),
...entryPaths,
...(entryPointsRaw ?? []),
@@ -572,17 +609,48 @@ export function expectBundled(id: string, opts: BundlerTestInput, dryRun?: boole
options: opts,
} satisfies BundlerTestBundleAPI;
+ // DCE keep scan
+ let keepMarkers: Record<string, number> = typeof dceKeepMarkerCount === "object" ? dceKeepMarkerCount : {};
+ let keepMarkersFound = 0;
+ if (dce && typeof dceKeepMarkerCount !== "number" && dceKeepMarkerCount !== false) {
+ for (const file of Object.entries(files)) {
+ keepMarkers[outfile ? outfile : path.join(outdir!, file[0]).slice(root.length).replace(/\.ts$/, ".js")] ??= [
+ ...file[1].matchAll(/KEEP/gi),
+ ].length;
+ }
+ }
+
// Check that the bundle failed with status code 0 by verifying all files exist.
+ // TODO: clean up this entire bit into one main loop
if (outfile) {
if (!existsSync(outfile)) {
throw new Error("Bundle was not written to disk: " + outfile);
} else {
const content = readFileSync(outfile).toString();
if (dce) {
- const dceFails = [...content.matchAll(/FAIL|FAILED|DROP|REMOVE/g)];
+ const dceFails = [...content.matchAll(/FAIL|FAILED|DROP|REMOVE/gi)];
if (dceFails.length) {
throw new Error("DCE test did not remove all expected code in " + outfile + ".");
}
+ if (dceKeepMarkerCount !== false) {
+ const keepMarkersThisFile = [...content.matchAll(/KEEP/gi)].length;
+ keepMarkersFound += keepMarkersThisFile;
+ if (
+ (typeof dceKeepMarkerCount === "number"
+ ? dceKeepMarkerCount
+ : Object.values(keepMarkers).reduce((a, b) => a + b, 0)) !== keepMarkersThisFile
+ ) {
+ throw new Error(
+ "DCE keep markers were not preserved in " +
+ outfile +
+ ". Expected " +
+ keepMarkers[outfile] +
+ " but found " +
+ keepMarkersThisFile +
+ ".",
+ );
+ }
+ }
}
if (!ESBUILD) {
// expect(readFileSync(outfile).toString()).toMatchSnapshot(outfile.slice(root.length));
@@ -590,18 +658,53 @@ export function expectBundled(id: string, opts: BundlerTestInput, dryRun?: boole
}
} else {
// entryNames makes it so we cannot predict the output file
- if (!entryNames) {
+ if (!entryNames || entryNames === "[name].[ext]") {
for (const fullpath of outputPaths) {
if (!existsSync(fullpath)) {
throw new Error("Bundle was not written to disk: " + fullpath);
- } else if (!ESBUILD) {
- // expect(readFileSync(fullpath).toString()).toMatchSnapshot(fullpath.slice(root.length));
+ } else {
+ if (!ESBUILD) {
+ // expect(readFileSync(fullpath).toString()).toMatchSnapshot(fullpath.slice(root.length));
+ }
+ if (dce) {
+ const content = readFileSync(fullpath, "utf8");
+ const dceFails = [...content.matchAll(/FAIL|FAILED|DROP|REMOVE/gi)];
+ const key = fullpath.slice(root.length);
+ if (dceFails.length) {
+ throw new Error("DCE test did not remove all expected code in " + key + ".");
+ }
+ if (dceKeepMarkerCount !== false) {
+ const keepMarkersThisFile = [...content.matchAll(/KEEP/gi)].length;
+ keepMarkersFound += keepMarkersThisFile;
+ if (keepMarkers[key] !== keepMarkersThisFile) {
+ throw new Error(
+ "DCE keep markers were not preserved in " +
+ key +
+ ". Expected " +
+ keepMarkers[key] +
+ " but found " +
+ keepMarkersThisFile +
+ ".",
+ );
+ }
+ }
+ }
}
}
} else if (!ESBUILD) {
// TODO: snapshot these test cases
}
}
+ if (
+ dce &&
+ dceKeepMarkerCount !== false &&
+ typeof dceKeepMarkerCount === "number" &&
+ keepMarkersFound !== dceKeepMarkerCount
+ ) {
+ throw new Error(
+ `DCE keep markers were not preserved. Expected ${dceKeepMarkerCount} KEEP markers, but found ${keepMarkersFound}.`,
+ );
+ }
if (assertNotPresent) {
for (const [key, value] of Object.entries(assertNotPresent)) {
diff --git a/test/bundler/report-bundler-test-progress.sh b/test/bundler/report-bundler-test-progress.sh
new file mode 100644
index 000000000..1ee0fec66
--- /dev/null
+++ b/test/bundler/report-bundler-test-progress.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+tests="$(echo esbuild/* bundler*.test.ts)"
+
+printf "%40s %7s %7s | %5s %5s %5s | %5s\n" "TEST" "defined" "refined" "pass" "fail" "skip" "%pass"
+
+total_defined=0
+total_total=0
+total_pass=0
+total_fail=0
+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)
+ 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)
+ if [ -z "$skip" ]; then skip=0; fi
+ if [ -z "$fail" ]; then fail=0; fi
+ if [ -z "$pass" ]; then pass=0; fi
+ total=$((pass + fail + skip))
+ percent_pass=$(echo "scale=1; ($pass * 100) / ($pass + $fail) " | bc 2>/dev/null || echo "-")
+ printf "%40s %7s %7s | %5s %5s %5s | %5s%%\n" "$test" "$defined" "$total" "$pass" "$fail" "$skip" "$percent_pass"
+
+ total_defined=$((total_defined + defined))
+ total_total=$((total_total + total))
+ total_pass=$((total_pass + pass))
+ total_fail=$((total_fail + fail))
+ total_skip=$((total_skip + skip))
+done
+
+total_pass_percent=$(echo "scale=1; ($total_pass * 100) / ($total_pass + $total_fail)")
+
+printf -- "\n"
+printf "%40s %7s %7s | %5s %5s %5s | %5s\n" "TOTAL" "$total_defined" "$total_total" "$total_pass" "$total_fail" "$total_skip" "$total_pass_percent"
+printf "\n"
+printf "\n"
+printf " %s%% Refined\n" $(echo "scale=1; $total_total / $total_defined * 100" | bc)
+printf " %s%% Passing\n" $(echo "scale=1; $total_pass / $total_total * 100" | bc)
+printf " %s%% Passing including what we skip\n" $(echo "scale=1; $total_pass / $total_total * 100" | bc)
+printf "\n"
+printf "dave's status: %s/%s tests\n" $total_total $total_defined
diff --git a/test/bundler/run-single-bundler-test.sh b/test/bundler/run-single-bundler-test.sh
index 5e9484dae..0b3cf96b2 100755
--- a/test/bundler/run-single-bundler-test.sh
+++ b/test/bundler/run-single-bundler-test.sh
@@ -6,7 +6,7 @@ if [ -z "$1" ]; then
exit 1
fi
-__dirname="$(dirname "$0")"
+__dirname="$(dirname $(realpath "$0"))"
cd "$__dirname"
clear
diff --git a/test/bundler/transpiler.test.js b/test/bundler/transpiler.test.js
index 0272fb7c6..da7a7d00d 100644
--- a/test/bundler/transpiler.test.js
+++ b/test/bundler/transpiler.test.js
@@ -2944,4 +2944,14 @@ console.log(foo, array);
expect(exports).toHaveLength(3);
});
});
+
+ describe("edge cases", () => {
+ it("import statement with quoted specifier", () => {
+ expectPrinted_(`import { "x.y" as xy } from "bar";`, `import {"x.y" as xy} from "bar"`);
+ });
+
+ it('`str` + "``"', () => {
+ expectPrinted_('const x = `str` + "``";', "const x = `str\\`\\``");
+ });
+ });
});
diff --git a/test/package.json b/test/package.json
index 7df5401be..f3ef067bd 100644
--- a/test/package.json
+++ b/test/package.json
@@ -10,7 +10,7 @@
"bktree-fast": "^0.0.7",
"body-parser": "^1.20.2",
"dedent": "^0.7.0",
- "esbuild": "^0.17.11",
+ "esbuild": "^0.17.16",
"express": "^4.18.2",
"iconv-lite": "^0.6.3",
"lodash": "^4.17.21",