aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2023-04-07 20:08:01 -0700
committerGravatar GitHub <noreply@github.com> 2023-04-07 20:08:01 -0700
commit6362414d65b69cd01624e84d08eca654fc8cb101 (patch)
tree77bda836b819769002b746da3c4b7bdb90546f41 /test
parentc0c5f07218096a52ed12d86374f785e6dc331fa4 (diff)
downloadbun-6362414d65b69cd01624e84d08eca654fc8cb101.tar.gz
bun-6362414d65b69cd01624e84d08eca654fc8cb101.tar.zst
bun-6362414d65b69cd01624e84d08eca654fc8cb101.zip
Bun gets a new bundler (#2312)
* alright now just gotta try running it * fix a gajillion compiler errors * even more code * okay i fixed more errors * wip * Update launch.json * Update string_builder.zig * `fast_debug_build_mode` makes debug build 2x faster * Update bundle_v2.zig * more code! * It bundles! * Rename `Bun.Transpiler` to `Bun.Bundler` * `import()` expressions almost work * wip attempt to get import() expr to work * Bundle namespace imports * Attempt to fix the issue with import() unsuccessfully * consider current working directory when resolving relative paths (#2313) * consider current working directory when resolving relative paths fixes #2298 * comment test --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> * support `expect().toThrow(/pattern/)` (#2314) - fix time-zone-dependent test failure * fix missing `Blob` error messages on Linux (#2315) * fix & clean up tests (#2318) - skip flaky tests when running as `root` - use `expect().toThrow()` - clean up temporary files after tests * feat(tty): add some `tty.WriteStream` methods to `process.{stdout, stderr}` (#2320) * feat(stdio): add some `tty.WriteStream` methods * chore(builtins): add process builtin gen'd code * Fix docker install command * `bun test` on macOS in GitHub Actions (#2322) * Fixes #2323 * throw invalid parameter errors in `crypto.scryptSync` (#2331) * throw invalid parameter errors * remove comptime, add empty buffer function * remove error_name comptime * Add reference documentation for bun:test (#2327) * Reorganize tests (#2332) * Fix html-rewriter.test.js * fix the wrong thing being incremented in hmr example (#2334) * Add more test harness * Improve Benchmarking page, small fixes (#2339) * Improve benchmarking page * WIP * Add typescript instructions to hot * Document preload in Plugins. Fix loader in plugin types. * Fix typo * Fix links * run prettier * Document openInEditor * improve `Buffer` compatibility with Node.js (#2341) * improve `Buffer` compatibility with Node.js * use `memmove()` allow `encoding` to be `undefined` * run `bun test` after macOS builds (#2343) * "binary" is an alias of "latin1" Fixes https://github.com/oven-sh/bun/issues/2110 * More spec compliant `Blob.prototype.type` (#2340) * Make `Blob.prototype. type` more spec compliant * Add a few more checks for isNumber() * Fix `make headers` * Safer JSValue.isString() * More tests for blob.slice * Make `Blob.prototype.type` more spec compliant * Add isASCII check * Fix types * Fix failing type test * Update blob.zig * Update blob.zig * Fix .eql check on empty values --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> * Fix bug in test runner * Support `import()` expressions * Implement `require()` * clean up bit_set.zig slightly * Move some things around * misc cleanup * Cleanup some things * Fix a lot of stuff * Fix `module.exports.fn = fn;` in ESM entry point * Fix crash due when printing file * Fix issue with class names * Fix issue with `export default identifier` * Update js_parser.zig * optimization: inline single-property object acceses and arrays * Fix undefined memory in renamed symbols list * Handle call target * wip * Inline it * Fix undefined memory issue when reclaiming blocks in ast * Halt linking on any parse errors * alias * Rename `enable_bundling` to `enable_legacy_bundling` * Workaround anonymous struct literal zig bug * Use slower approach (without bitset) because it doesn't break after 8 symbols * Fix incorrectly-renaming statically defined symbols * Handle more edgecases in our bit_set fork * Reduce number of allocations for `define` * Do not rename unbound symbols * Clean up dot defines a little more * Make the generated names prettier * Workaround runtime symbol missing issue * Fail the build on errors * Support export * from * Support `--outfile` * partially fix renaming * fanicer symbol renaming impl * misc, extremely revertible cleanup * Fix up some bugs with symbol renaming * formatting * Update launch.json * Parse `__PURE__` comments * clean up simd code for pure comments * changes to merge * workaround runtime issue * Fix issue with `export * as` not propagating correctly * Make all top-level declarations `var` when bundling * Fix missing prefix * Fix assigning to stack copy * Fix missing runtime symbol * Fix bug with namespace exports * Dramatically reduce allocations * Update launch.json * Add missing flags * Update js_parser.zig * small cleanup * Make the export name better * Fix unnecessary `var foo = foo` * Implement CommonJS -> ESM conversion * Implement module redirects * Port esbuild bundler tests for new bundler (#2380) * started porting esbuild tests * clean up test names and api before moving on * port tests using a program i wrote * replace todo generated comment * fix generated tests not including some files * work on tests * [github web editor] add define, external, inject, minifySyntax, minifyWhitespace options. * get most of the todo comments out of the way, but expectBundled does not handle most of the cases * continue working on esbuild tests * use test.skip for unsupported tests * Fixups for test runner * Hoist imports & exports * Fix test * Hoist classes * bundler test refining, 51/835 * Fix runtime require * bundler test refining, 81/835 * bundler test refining, 93/835 * Make the test work in any timezone * feat(expect): update toBeInstanceOf (#2396) * feat: update instanceof binding * fix: according to PR comments * Rename `expectObjectTypeCount` to `expectMaxObjectTypeCount` * Fix socket tests with connection errors (#2403) * release pending activity with connection error handler * unref poll_ref * remove trailing comma * Organize Dockerfiles for official status * Remove test Dockerfile * Remove old Docker workflow * Feat(test): add toMatch (#2404) * Fix various fetch/response/request tests (#2416) * fix most fetch tests, skip a few * fastGet, toValueGC, and invalid init * bigint unreachable, range error, log process as process * remove extra fetch_headers * remove js_type parameter, check isObject() * throw invalid mime type error, use enum literal * switch back to promise rejection * RangeError pascal case * Fix several bugs (#2418) * utf16 codepoint with replacement character * Fix test failure with `TextEncoder("ascii')` * Add missing type * Fix Response.prototype.bodyUsed and Request.prototype.bodyUsed * Fix bug with scrypt error not clearing * Update server.zig * oopsie * :nail_care: * docs: Use correct url in the 'Issues' link in README header (#2420) * Fix crash when rendering error page and the server or network is slow * [fetch] Make the default body value `null` when unspecified This is better aligned with the fetch spec * Make node-net tests less flaky * [node:net] Fix issue with `listen` callback firing before it's listening * Always clear timers in node test harness * Fix out of bounds access Repro'd in Buffer tests * Update UWS cc @cirospaciari * Make this test more thorough * Hanging abort test * 0 length body is a null stream * Several bug fixes (#2427) * Fix test * Fix segfault when unexpected type is passed in `expect().toThrow` * Fix issues with request constructor * Don't bother cloning headers when its empty * woops * more tests * fix incorrect test * Make the fetch error messages better * Update response.zig * Fix test that failed on macOS * Fix test * Remove extra hash table lookups * Support running dummy registry directly cc @alexlamsl * Update test * Update test * fixup * Workaround crash in test runner * Fixup test * Fixup test * Update os.test.js --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> * Remove usages of port numbers in tests * Set -O2 and -fno-rtti * Remove -g * Prevent undefined memory access * [bun test] Implement `--rerun-each` flag to run each test N times * Reduce number of module scopes created * add some extra abort checks into streams (#2430) * add some checks to avoid UAF * avoid multiple calls to finalize if endFromJS is called more than once * fix no-op comment * mark as requested_end on abort * remove requested_end from abort * remove unnecessary check (#2432) * Fix bug with scoped aliased dependencies in bun install on macOS * remove `addLog`, remove `--prominent-compile-errors` * Finish the upgrade * Optional chaining flag * Implement same_target_becomes_destructuring optimization * bundler test refining, 109/835 * Reset bindings * Support multiple entry points * Implement `--entry-names` flag * Use a tempdir with a better name * prettier * Log file name * Update js_parser.zig * Mark all bun builtins as external * Make resolve errors actually errors * Update bundler_default.test.ts * Fix `await import(foo)` * WIP react server components * Do more stuff at runtime * :scissors: * Support automatic JSX imports * Use a module cache for now * Update tsconfig.base.json * Fix ThisOutsideFunctionNotRenamed * woopsie * moar cpu * clamp it * fixup * Add a bunch of assertions * Bun uses automatic runtime by default * Parse Import Attributes * Add a note about Valgrind * Update developing.md * Fix up code splitting for React Server Components * Implement client component manifest * Fix crash with --react-server-components and no client components * Backport https://github.com/ziglang/zig/commit/4d31e3c917a05541394c544708f0047cfb53331a * Update launch.json * Fix for latest zig * Workaround bug with ?[]const string Occasionally saw alignment errors in this code Workaround https://github.com/ziglang/zig/issues/15085 related: https://github.com/ziglang/zig/pull/15089 * switch to regular slice * Avoid initializing named_imports and named_exports as undefined * Reduce usages of `undefined` * Add more assertions * --watch wip * Update javascript.zig * Possibly fix the race condition * Faster `do` * bump allocator * Reduce the size of `Symbol` slightly * Alphabetically sort runtime import symbols, for determinism * Prepare for code splitting * handle overlapping stdout * pure * clean up some things * Fix bug with `$$typeof` * Address CommonJS -> ESM hoisting bug * Support `"use server"` in manifest * Implement `"use server"` * Fix importing bun builtins when bundling * Make `commonjs_to_esm` a feature flag, fix some splitting bugs * :scissors: * fixme remove this * Fix crash in longestCommonPath * Chunking! Just need to do import paths now. * Import paths work...now trying to figure out how to make runtime symbols work * add workaround * Replace `bun bun` with `bun build` * Fix crash with dual package hazard * Fix many CommonJS <> ESM interop bugs * Support package.json `"sideEffects"` also skip loading unnecessary package.json data in `bun run` * add a not good --watch implementation * bundler test refining, 140/831 * remove accidentally committed file * do not return status code 1 on successful bundles * bundler test refining, 159/830 * pass exit code to exitOrWatch * clean up help menu -remove two spaces to line up bun build -moved all <r> tags to the end of the text they are colorizing -moved other colors to the start of the text they colorize -removed unneeded <r> tags, keeping only one at the start of the block * importstar is fully ported * wip * you can run code in this branch now * Disable this transform * organize and document bundler tests * Fix double import * Fix sloppy mode function declarations * Disable our CommonJS transform for now * add `assertNotPresent` to make splitting cases easier * Bump! * Update bun.d.ts * use import.meta.require in runtime code * Disable this again * Fix dirname * Fix ESM -> CJS wrapper * :nail_care: --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> Co-authored-by: Alex Lam S.L <alexlamsl@gmail.com> Co-authored-by: Derrick Farris <mr.dcfarris@gmail.com> Co-authored-by: Ashcon Partovi <ashcon@partovi.net> Co-authored-by: Dylan Conway <35280289+dylan-conway@users.noreply.github.com> Co-authored-by: pfg <pfg@pfg.pw> Co-authored-by: Colin McDonnell <colinmcd94@gmail.com> Co-authored-by: dave caruso <me@paperdave.net> Co-authored-by: zhiyuan <32867472+zhiyuang@users.noreply.github.com> Co-authored-by: Dylan Conway <dylan.conway567@gmail.com> Co-authored-by: Kamil Ogórek <kamil.ogorek@gmail.com> Co-authored-by: Ciro Spaciari <ciro.spaciari@gmail.com>
Diffstat (limited to 'test')
-rwxr-xr-xtest/bun.lockbbin36018 -> 36614 bytes
-rw-r--r--test/bundler/bundler_edgecase.test.ts26
-rw-r--r--test/bundler/esbuild/css.test.ts527
-rw-r--r--test/bundler/esbuild/dce.test.ts2749
-rw-r--r--test/bundler/esbuild/default.test.ts6136
-rw-r--r--test/bundler/esbuild/importstar.test.ts1346
-rw-r--r--test/bundler/esbuild/importstar_ts.test.ts305
-rw-r--r--test/bundler/esbuild/loader.test.ts771
-rw-r--r--test/bundler/esbuild/lower.test.ts1809
-rw-r--r--test/bundler/esbuild/packagejson.test.ts1879
-rw-r--r--test/bundler/esbuild/splitting.test.ts390
-rw-r--r--test/bundler/esbuild/ts.test.ts1638
-rw-r--r--test/bundler/esbuild/tsconfig.test.ts1565
-rw-r--r--test/bundler/expectBundled.md184
-rw-r--r--test/bundler/expectBundled.ts760
-rwxr-xr-xtest/bundler/run-single-bundler-test.sh36
-rw-r--r--test/fixtures/bundle/trivial/fn.js3
-rw-r--r--test/fixtures/bundle/trivial/index.js5
-rw-r--r--test/js/bun/resolve/import-meta.test.js8
-rw-r--r--test/js/deno/html/blob.test.ts114
-rw-r--r--test/package.json5
21 files changed, 20251 insertions, 5 deletions
diff --git a/test/bun.lockb b/test/bun.lockb
index 267386e00..f95cf3658 100755
--- a/test/bun.lockb
+++ b/test/bun.lockb
Binary files differ
diff --git a/test/bundler/bundler_edgecase.test.ts b/test/bundler/bundler_edgecase.test.ts
new file mode 100644
index 000000000..d57fd3281
--- /dev/null
+++ b/test/bundler/bundler_edgecase.test.ts
@@ -0,0 +1,26 @@
+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", () => {
+ itBundled("edgecase/EmptyFile", {
+ files: {
+ "/entry.js": "",
+ },
+ });
+ itBundled("edgecase/ImportStarFunction", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as foo from "./foo.js";
+ console.log(foo.fn());
+ `,
+ "/foo.js": /* js */ `
+ export function fn() {
+ return "foo";
+ }
+ `,
+ },
+ run: { stdout: "foo" },
+ });
+});
diff --git a/test/bundler/esbuild/css.test.ts b/test/bundler/esbuild/css.test.ts
new file mode 100644
index 000000000..2e0798082
--- /dev/null
+++ b/test/bundler/esbuild/css.test.ts
@@ -0,0 +1,527 @@
+import { expectBundled, itBundled, testForFile } from "../expectBundled";
+var { describe, test, expect } = testForFile(import.meta.path);
+
+// Tests ported from:
+// https://github.com/evanw/esbuild/blob/main/internal/bundler_tests/bundler_css_test.go
+
+// For debug, all files are written to $TEMP/bun-bundle-tests/css
+
+// describe("bundler", () => {
+// itBundled("css/CSSEntryPoint", {
+// // GENERATED
+// files: {
+// "/entry.css": /* css */ `
+// body {
+// background: white;
+// color: black }
+// `,
+// },
+// });
+// itBundled("css/CSSAtImportMissing", {
+// files: {
+// "/entry.css": `@import "./missing.css";`,
+// },
+// bundleErrors: {
+// "/entry.css": ['Could not resolve "./missing.css"'],
+// },
+// });
+// itBundled("css/CSSAtImportExternal", {
+// // GENERATED
+// files: {
+// "/entry.css": /* css */ `
+// @import "./internal.css";
+// @import "./external1.css";
+// @import "./external2.css";
+// @import "./charset1.css";
+// @import "./charset2.css";
+// @import "./external5.css" screen;
+// `,
+// "/internal.css": /* css */ `
+// @import "./external5.css" print;
+// .before { color: red }
+// `,
+// "/charset1.css": /* css */ `
+// @charset "UTF-8";
+// @import "./external3.css";
+// @import "./external4.css";
+// @import "./external5.css";
+// @import "https://www.example.com/style1.css";
+// @import "https://www.example.com/style2.css";
+// @import "https://www.example.com/style3.css" print;
+// .middle { color: green }
+// `,
+// "/charset2.css": /* css */ `
+// @charset "UTF-8";
+// @import "./external3.css";
+// @import "./external5.css" screen;
+// @import "https://www.example.com/style1.css";
+// @import "https://www.example.com/style3.css";
+// .after { color: blue }
+// `,
+// },
+// });
+// itBundled("css/CSSAtImport", {
+// // GENERATED
+// files: {
+// "/entry.css": /* css */ `
+// @import "./a.css";
+// @import "./b.css";
+// .entry { color: red }
+// `,
+// "/a.css": /* css */ `
+// @import "./shared.css";
+// .a { color: green }
+// `,
+// "/b.css": /* css */ `
+// @import "./shared.css";
+// .b { color: blue }
+// `,
+// "/shared.css": `.shared { color: black }`,
+// },
+// });
+// itBundled("css/CSSFromJSMissingImport", {
+// // GENERATED
+// files: {
+// "/entry.js": /* js */ `
+// import {missing} from "./a.css"
+// console.log(missing)
+// `,
+// "/a.css": `.a { color: red }`,
+// },
+// /* TODO FIX expectedCompileLog: `entry.js: ERROR: No matching export in "a.css" for import "missing"
+// `, */
+// });
+// itBundled("css/CSSFromJSMissingStarImport", {
+// // GENERATED
+// files: {
+// "/entry.js": /* js */ `
+// import * as ns from "./a.css"
+// console.log(ns.missing)
+// `,
+// "/a.css": `.a { color: red }`,
+// },
+// });
+// itBundled("css/ImportCSSFromJS", {
+// // GENERATED
+// files: {
+// "/entry.js": /* js */ `
+// import "./a.js"
+// import "./b.js"
+// `,
+// "/a.js": /* js */ `
+// import "./a.css";
+// console.log('a')
+// `,
+// "/a.css": `.a { color: red }`,
+// "/b.js": /* js */ `
+// import "./b.css";
+// console.log('b')
+// `,
+// "/b.css": `.b { color: blue }`,
+// },
+// });
+// itBundled("css/ImportCSSFromJSWriteToStdout", {
+// // GENERATED
+// files: {
+// "/entry.js": `import "./entry.css"`,
+// "/entry.css": `.entry { color: red }`,
+// },
+// /* TODO FIX expectedScanLog: `entry.js: ERROR: Cannot import "entry.css" into a JavaScript file without an output path configured
+// `, */
+// });
+// itBundled("css/ImportJSFromCSS", {
+// // GENERATED
+// files: {
+// "/entry.js": `export default 123`,
+// "/entry.css": `@import "./entry.js";`,
+// },
+// entryPoints: ["/entry.css"],
+// /* TODO FIX expectedScanLog: `entry.css: ERROR: Cannot import "entry.js" into a CSS file
+// NOTE: An "@import" rule can only be used to import another CSS file, and "entry.js" is not a CSS file (it was loaded with the "js" loader).
+// `, */
+// });
+// itBundled("css/ImportJSONFromCSS", {
+// // GENERATED
+// files: {
+// "/entry.json": `{}`,
+// "/entry.css": `@import "./entry.json";`,
+// },
+// entryPoints: ["/entry.css"],
+// /* TODO FIX expectedScanLog: `entry.css: ERROR: Cannot import "entry.json" into a CSS file
+// NOTE: An "@import" rule can only be used to import another CSS file, and "entry.json" is not a CSS file (it was loaded with the "json" loader).
+// `, */
+// });
+// itBundled("css/MissingImportURLInCSS", {
+// // GENERATED
+// files: {
+// "/src/entry.css": /* css */ `
+// a { background: url(./one.png); }
+// b { background: url("./two.png"); }
+// `,
+// },
+// /* TODO FIX expectedScanLog: `src/entry.css: ERROR: Could not resolve "./one.png"
+// src/entry.css: ERROR: Could not resolve "./two.png"
+// `, */
+// });
+// itBundled("css/ExternalImportURLInCSS", {
+// // GENERATED
+// files: {
+// "/src/entry.css": /* css */ `
+// div:after {
+// content: 'If this is recognized, the path should become "../src/external.png"';
+// background: url(./external.png);
+// }
+
+// /* These URLs should be external automatically */
+// a { background: url(http://example.com/images/image.png) }
+// b { background: url(https://example.com/images/image.png) }
+// c { background: url(//example.com/images/image.png) }
+// d { background: url(data:image/png;base64,iVBORw0KGgo=) }
+// path { fill: url(#filter) }
+// `,
+// },
+// });
+// itBundled("css/InvalidImportURLInCSS", {
+// // GENERATED
+// files: {
+// "/entry.css": /* css */ `
+// a {
+// background: url(./js.js);
+// background: url("./jsx.jsx");
+// background: url(./ts.ts);
+// background: url('./tsx.tsx');
+// background: url(./json.json);
+// background: url(./css.css);
+// }
+// `,
+// "/js.js": `export default 123`,
+// "/jsx.jsx": `export default 123`,
+// "/ts.ts": `export default 123`,
+// "/tsx.tsx": `export default 123`,
+// "/json.json": `{ "test": true }`,
+// "/css.css": `a { color: red }`,
+// },
+// /* TODO FIX expectedScanLog: `entry.css: ERROR: Cannot use "js.js" as a URL
+// NOTE: You can't use a "url()" token to reference the file "js.js" because it was loaded with the "js" loader, which doesn't provide a URL to embed in the resulting CSS.
+// entry.css: ERROR: Cannot use "jsx.jsx" as a URL
+// NOTE: You can't use a "url()" token to reference the file "jsx.jsx" because it was loaded with the "jsx" loader, which doesn't provide a URL to embed in the resulting CSS.
+// entry.css: ERROR: Cannot use "ts.ts" as a URL
+// NOTE: You can't use a "url()" token to reference the file "ts.ts" because it was loaded with the "ts" loader, which doesn't provide a URL to embed in the resulting CSS.
+// entry.css: ERROR: Cannot use "tsx.tsx" as a URL
+// NOTE: You can't use a "url()" token to reference the file "tsx.tsx" because it was loaded with the "tsx" loader, which doesn't provide a URL to embed in the resulting CSS.
+// entry.css: ERROR: Cannot use "json.json" as a URL
+// NOTE: You can't use a "url()" token to reference the file "json.json" because it was loaded with the "json" loader, which doesn't provide a URL to embed in the resulting CSS.
+// entry.css: ERROR: Cannot use "css.css" as a URL
+// NOTE: You can't use a "url()" token to reference a CSS file, and "css.css" is a CSS file (it was loaded with the "css" loader).
+// `, */
+// });
+// itBundled("css/TextImportURLInCSSText", {
+// // GENERATED
+// files: {
+// "/entry.css": /* css */ `
+// a {
+// background: url(./example.txt);
+// }
+// `,
+// "/example.txt": `This is some text.`,
+// },
+// });
+// itBundled("css/DataURLImportURLInCSS", {
+// // GENERATED
+// files: {
+// "/entry.css": /* css */ `
+// a {
+// background: url(./example.png);
+// }
+// `,
+// "/example.png": `\x89\x50\x4E\x47\x0D\x0A\x1A\x0A`,
+// },
+// });
+// itBundled("css/BinaryImportURLInCSS", {
+// // GENERATED
+// files: {
+// "/entry.css": /* css */ `
+// a {
+// background: url(./example.png);
+// }
+// `,
+// "/example.png": `\x89\x50\x4E\x47\x0D\x0A\x1A\x0A`,
+// },
+// });
+// itBundled("css/Base64ImportURLInCSS", {
+// // GENERATED
+// files: {
+// "/entry.css": /* css */ `
+// a {
+// background: url(./example.png);
+// }
+// `,
+// "/example.png": `\x89\x50\x4E\x47\x0D\x0A\x1A\x0A`,
+// },
+// });
+// itBundled("css/FileImportURLInCSS", {
+// // GENERATED
+// files: {
+// "/entry.css": /* css */ `
+// @import "./one.css";
+// @import "./two.css";
+// `,
+// "/one.css": `a { background: url(./example.data) }`,
+// "/two.css": `b { background: url(./example.data) }`,
+// "/example.data": `This is some data.`,
+// },
+// });
+// itBundled("css/IgnoreURLsInAtRulePrelude", {
+// // GENERATED
+// files: {
+// "/entry.css": /* css */ `
+// /* This should not generate a path resolution error */
+// @supports (background: url(ignored.png)) {
+// a { color: red }
+// }
+// `,
+// },
+// });
+// itBundled("css/PackageURLsInCSS", {
+// // GENERATED
+// files: {
+// "/entry.css": /* css */ `
+// @import "test.css";
+
+// a { background: url(a/1.png); }
+// b { background: url(b/2.png); }
+// c { background: url(c/3.png); }
+// `,
+// "/test.css": `.css { color: red }`,
+// "/a/1.png": `a-1`,
+// "/node_modules/b/2.png": `b-2-node_modules`,
+// "/c/3.png": `c-3`,
+// "/node_modules/c/3.png": `c-3-node_modules`,
+// },
+// });
+// itBundled("css/CSSAtImportExtensionOrderCollision", {
+// // GENERATED
+// files: {
+// "/entry.css": `@import "./test";`,
+// "/test.js": `console.log('js')`,
+// "/test.css": `.css { color: red }`,
+// },
+// outfile: "/out.css",
+// extensionOrder: [".js", ".css"],
+// });
+// itBundled("css/CSSAtImportExtensionOrderCollisionUnsupported", {
+// // GENERATED
+// files: {
+// "/entry.css": `@import "./test";`,
+// "/test.js": `console.log('js')`,
+// "/test.sass": `// some code`,
+// },
+// outfile: "/out.css",
+// extensionOrder: [".js", ".sass"],
+// bundleErrors: {
+// "/entry.css": ['ERROR: No loader is configured for ".sass" files: test.sass'],
+// },
+// });
+// itBundled("css/CSSAtImportConditionsNoBundle", {
+// // GENERATED
+// files: {
+// "/entry.css": `@import "./print.css" print;`,
+// },
+// mode: "passthrough",
+// });
+// itBundled("css/CSSAtImportConditionsBundleExternal", {
+// // GENERATED
+// files: {
+// "/entry.css": `@import "https://example.com/print.css" print;`,
+// },
+// });
+// itBundled("css/CSSAtImportConditionsBundleExternalConditionWithURL", {
+// // GENERATED
+// files: {
+// "/entry.css": `@import "https://example.com/foo.css" (foo: url("foo.png")) and (bar: url("bar.png"));`,
+// },
+// });
+// itBundled("css/CSSAtImportConditionsBundle", {
+// // GENERATED
+// files: {
+// "/entry.css": `@import "./print.css" print;`,
+// "/print.css": `body { color: red }`,
+// },
+// /* TODO FIX expectedScanLog: `entry.css: ERROR: Bundling with conditional "@import" rules is not currently supported
+// `, */
+// });
+// itBundled("css/CSSAndJavaScriptCodeSplittingIssue1064", {
+// // GENERATED
+// files: {
+// "/a.js": /* js */ `
+// import shared from './shared.js'
+// console.log(shared() + 1)
+// `,
+// "/b.js": /* js */ `
+// import shared from './shared.js'
+// console.log(shared() + 2)
+// `,
+// "/c.css": /* css */ `
+// @import "./shared.css";
+// body { color: red }
+// `,
+// "/d.css": /* css */ `
+// @import "./shared.css";
+// body { color: blue }
+// `,
+// "/shared.js": `export default function() { return 3 }`,
+// "/shared.css": `body { background: black }`,
+// },
+// entryPoints: ["/a.js", "/b.js", "/c.css", "/d.css"],
+// format: "esm",
+// splitting: true,
+// });
+// itBundled("css/CSSExternalQueryAndHashNoMatchIssue1822", {
+// // GENERATED
+// files: {
+// "/entry.css": /* css */ `
+// a { background: url(foo/bar.png?baz) }
+// b { background: url(foo/bar.png#baz) }
+// `,
+// },
+// outfile: "/out.css",
+// /* TODO FIX expectedScanLog: `entry.css: ERROR: Could not resolve "foo/bar.png?baz"
+// NOTE: You can mark the path "foo/bar.png?baz" as external to exclude it from the bundle, which will remove this error.
+// entry.css: ERROR: Could not resolve "foo/bar.png#baz"
+// NOTE: You can mark the path "foo/bar.png#baz" as external to exclude it from the bundle, which will remove this error.
+// `, */
+// });
+// itBundled("css/CSSExternalQueryAndHashMatchIssue1822", {
+// // GENERATED
+// files: {
+// "/entry.css": /* css */ `
+// a { background: url(foo/bar.png?baz) }
+// b { background: url(foo/bar.png#baz) }
+// `,
+// },
+// outfile: "/out.css",
+// });
+// itBundled("css/CSSNestingOldBrowser", {
+// // GENERATED
+// files: {
+// "/nested-@layer.css": `a { @layer base { color: red; } }`,
+// "/nested-@media.css": `a { @media screen { color: red; } }`,
+// "/nested-ampersand-twice.css": `a { &, & { color: red; } }`,
+// "/nested-ampersand-first.css": `a { &, b { color: red; } }`,
+// "/nested-attribute.css": `a { [href] { color: red; } }`,
+// "/nested-colon.css": `a { :hover { color: red; } }`,
+// "/nested-dot.css": `a { .cls { color: red; } }`,
+// "/nested-greaterthan.css": `a { > b { color: red; } }`,
+// "/nested-hash.css": `a { #id { color: red; } }`,
+// "/nested-plus.css": `a { + b { color: red; } }`,
+// "/nested-tilde.css": `a { ~ b { color: red; } }`,
+// "/toplevel-ampersand-twice.css": `&, & { color: red; }`,
+// "/toplevel-ampersand-first.css": `&, a { color: red; }`,
+// "/toplevel-ampersand-second.css": `a, & { color: red; }`,
+// "/toplevel-attribute.css": `[href] { color: red; }`,
+// "/toplevel-colon.css": `:hover { color: red; }`,
+// "/toplevel-dot.css": `.cls { color: red; }`,
+// "/toplevel-greaterthan.css": `> b { color: red; }`,
+// "/toplevel-hash.css": `#id { color: red; }`,
+// "/toplevel-plus.css": `+ b { color: red; }`,
+// "/toplevel-tilde.css": `~ b { color: red; }`,
+// },
+// entryPoints: [
+// "/nested-@layer.css",
+// "/nested-@media.css",
+// "/nested-ampersand-twice.css",
+// "/nested-ampersand-first.css",
+// "/nested-attribute.css",
+// "/nested-colon.css",
+// "/nested-dot.css",
+// "/nested-greaterthan.css",
+// "/nested-hash.css",
+// "/nested-plus.css",
+// "/nested-tilde.css",
+// "/toplevel-ampersand-twice.css",
+// "/toplevel-ampersand-first.css",
+// "/toplevel-ampersand-second.css",
+// "/toplevel-attribute.css",
+// "/toplevel-colon.css",
+// "/toplevel-dot.css",
+// "/toplevel-greaterthan.css",
+// "/toplevel-hash.css",
+// "/toplevel-plus.css",
+// "/toplevel-tilde.css",
+// ],
+// unsupportedCSSFeatures: "Nesting",
+// /* TODO FIX expectedScanLog: `nested-@layer.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
+// nested-@media.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
+// nested-ampersand-first.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
+// nested-ampersand-twice.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
+// nested-attribute.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
+// nested-colon.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
+// nested-dot.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
+// nested-greaterthan.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
+// nested-hash.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
+// nested-plus.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
+// nested-tilde.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
+// toplevel-ampersand-first.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
+// toplevel-ampersand-second.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
+// toplevel-ampersand-twice.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
+// toplevel-ampersand-twice.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
+// toplevel-greaterthan.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
+// toplevel-plus.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
+// toplevel-tilde.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
+// `, */
+// });
+// itBundled("css/MetafileCSSBundleTwoToOne", {
+// files: {
+// "/foo/entry.js": /* js */ `
+// import '../common.css'
+// console.log('foo')
+// `,
+// "/bar/entry.js": /* js */ `
+// import '../common.css'
+// console.log('bar')
+// `,
+// "/common.css": `body { color: red }`,
+// },
+// metafile: true,
+// entryPoints: ["/foo/entry.js", "/bar/entry.js"],
+// entryNames: "[ext]/[hash]",
+// outdir: "/",
+// });
+// itBundled("css/DeduplicateRules", {
+// // GENERATED
+// files: {
+// "/yes0.css": `a { color: red; color: green; color: red }`,
+// "/yes1.css": `a { color: red } a { color: green } a { color: red }`,
+// "/yes2.css": `@media screen { a { color: red } } @media screen { a { color: red } }`,
+// "/no0.css": `@media screen { a { color: red } } @media screen { & a { color: red } }`,
+// "/no1.css": `@media screen { a { color: red } } @media screen { a[x] { color: red } }`,
+// "/no2.css": `@media screen { a { color: red } } @media screen { a.x { color: red } }`,
+// "/no3.css": `@media screen { a { color: red } } @media screen { a#x { color: red } }`,
+// "/no4.css": `@media screen { a { color: red } } @media screen { a:x { color: red } }`,
+// "/no5.css": `@media screen { a:x { color: red } } @media screen { a:x(y) { color: red } }`,
+// "/no6.css": `@media screen { a b { color: red } } @media screen { a + b { color: red } }`,
+// "/across-files.css": `@import 'across-files-0.css'; @import 'across-files-1.css'; @import 'across-files-2.css';`,
+// "/across-files-0.css": `a { color: red; color: red }`,
+// "/across-files-1.css": `a { color: green }`,
+// "/across-files-2.css": `a { color: red }`,
+// "/across-files-url.css": `@import 'across-files-url-0.css'; @import 'across-files-url-1.css'; @import 'across-files-url-2.css';`,
+// "/across-files-url-0.css": `@import 'http://example.com/some.css'; @font-face { src: url(http://example.com/some.font); }`,
+// "/across-files-url-1.css": `@font-face { src: url(http://example.com/some.other.font); }`,
+// "/across-files-url-2.css": `@font-face { src: url(http://example.com/some.font); }`,
+// },
+// entryPoints: [
+// "/yes0.css",
+// "/yes1.css",
+// "/yes2.css",
+// "/no0.css",
+// "/no1.css",
+// "/no2.css",
+// "/no3.css",
+// "/no4.css",
+// "/no5.css",
+// "/no6.css",
+// "/across-files.css",
+// "/across-files-url.css",
+// ],
+// });
+// });
diff --git a/test/bundler/esbuild/dce.test.ts b/test/bundler/esbuild/dce.test.ts
new file mode 100644
index 000000000..2cd8a971b
--- /dev/null
+++ b/test/bundler/esbuild/dce.test.ts
@@ -0,0 +1,2749 @@
+import { expectBundled, itBundled, testForFile } from "../expectBundled";
+var { describe, test, expect } = testForFile(import.meta.path);
+
+// Tests ported from:
+// https://github.com/evanw/esbuild/blob/main/internal/bundler_tests/bundler_dce_test.go
+
+// For debug, all files are written to $TEMP/bun-bundle-tests/dce
+
+describe("bundler", () => {
+ return;
+ itBundled("dce/PackageJsonSideEffectsFalseKeepNamedImportES6", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import {foo} from "demo-pkg"
+ console.log(foo)
+ `,
+ "/Users/user/project/node_modules/demo-pkg/index.js": /* js */ `
+ export const foo = 123
+ console.log('hello')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "sideEffects": false
+ }
+ `,
+ },
+ });
+ itBundled("dce/PackageJsonSideEffectsFalseKeepNamedImportCommonJS", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import {foo} from "demo-pkg"
+ console.log(foo)
+ `,
+ "/Users/user/project/node_modules/demo-pkg/index.js": /* js */ `
+ exports.foo = 123
+ console.log('hello')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "sideEffects": false
+ }
+ `,
+ },
+ });
+ itBundled("dce/PackageJsonSideEffectsFalseKeepStarImportES6", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import * as ns from "demo-pkg"
+ console.log(ns)
+ `,
+ "/Users/user/project/node_modules/demo-pkg/index.js": /* js */ `
+ export const foo = 123
+ console.log('hello')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "sideEffects": false
+ }
+ `,
+ },
+ });
+ itBundled("dce/PackageJsonSideEffectsFalseKeepStarImportCommonJS", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import * as ns from "demo-pkg"
+ console.log(ns)
+ `,
+ "/Users/user/project/node_modules/demo-pkg/index.js": /* js */ `
+ exports.foo = 123
+ console.log('hello')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "sideEffects": false
+ }
+ `,
+ },
+ });
+ 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
+ console.log('hello')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "sideEffects": true
+ }
+ `,
+ },
+ });
+ itBundled("dce/PackageJsonSideEffectsTrueKeepCommonJS", {
+ // 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')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "sideEffects": true
+ }
+ `,
+ },
+ });
+ itBundled("dce/PackageJsonSideEffectsFalseKeepBareImportAndRequireES6", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import "demo-pkg"
+ require('demo-pkg')
+ console.log('unused import')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/index.js": /* js */ `
+ export const foo = 123
+ console.log('hello')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "sideEffects": false
+ }
+ `,
+ },
+ /* 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:
+ `, */
+ });
+ itBundled("dce/PackageJsonSideEffectsFalseKeepBareImportAndRequireCommonJS", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import "demo-pkg"
+ require('demo-pkg')
+ console.log('unused import')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/index.js": /* js */ `
+ exports.foo = 123
+ console.log('hello')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "sideEffects": false
+ }
+ `,
+ },
+ /* 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:
+ `, */
+ });
+ 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')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "sideEffects": false
+ }
+ `,
+ },
+ /* 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:
+ `, */
+ });
+ 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')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "sideEffects": false
+ }
+ `,
+ },
+ /* 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:
+ `, */
+ });
+ 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')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "sideEffects": false
+ }
+ `,
+ },
+ });
+ 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')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "sideEffects": false
+ }
+ `,
+ },
+ });
+ 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')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "sideEffects": false
+ }
+ `,
+ },
+ });
+ 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')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "sideEffects": false
+ }
+ `,
+ },
+ });
+ 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
+ console.log('hello')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "sideEffects": []
+ }
+ `,
+ },
+ });
+ 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')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "sideEffects": ["./index.js"]
+ }
+ `,
+ },
+ });
+ 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
+ console.log('TEST FAILED')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/index-module.js": /* js */ `
+ export const foo = 123
+ console.log('TEST FAILED')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "main": "index-main.js",
+ "module": "index-module.js",
+ "sideEffects": ["./index-main.js"]
+ }
+ `,
+ },
+ });
+ 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
+ console.log('this should be kept')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/index-module.js": /* js */ `
+ export const foo = 123
+ console.log('TEST FAILED')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "main": "index-main.js",
+ "module": "index-module.js",
+ "sideEffects": ["./index-main.js"]
+ }
+ `,
+ },
+ });
+ 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
+ console.log('TEST FAILED')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/index-module.js": /* js */ `
+ export const foo = 123
+ console.log('TEST FAILED')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "main": "index-main.js",
+ "module": "index-module.js",
+ "sideEffects": ["./index-main.js"]
+ }
+ `,
+ },
+ });
+ itBundled("dce/PackageJsonSideEffectsArrayKeepMainImplicitMain", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import {foo} from "demo-pkg"
+ import "./require-demo-pkg"
+ console.log('unused import')
+ `,
+ "/Users/user/project/src/require-demo-pkg.js": /* js */ `
+ // This causes "index-main.js" to be selected
+ require('demo-pkg')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/index-main.js": /* js */ `
+ export const foo = 123
+ console.log('this should be kept')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/index-module.js": /* js */ `
+ export const foo = 123
+ console.log('TEST FAILED')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "main": "index-main.js",
+ "module": "index-module.js",
+ "sideEffects": ["./index-main.js"]
+ }
+ `,
+ },
+ });
+ 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
+ console.log('TEST FAILED')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/index-module.js": /* js */ `
+ export const foo = 123
+ console.log('this should be kept')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "main": "index-main.js",
+ "module": "index-module.js",
+ "sideEffects": ["./index-module.js"]
+ }
+ `,
+ },
+ });
+ itBundled("dce/PackageJsonSideEffectsArrayKeepModuleUseMain", {
+ // 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
+ console.log('TEST FAILED')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/index-module.js": /* js */ `
+ export const foo = 123
+ console.log('TEST FAILED')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "main": "index-main.js",
+ "module": "index-module.js",
+ "sideEffects": ["./index-module.js"]
+ }
+ `,
+ },
+ });
+ itBundled("dce/PackageJsonSideEffectsArrayKeepModuleImplicitModule", {
+ // 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
+ console.log('TEST FAILED')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/index-module.js": /* js */ `
+ export const foo = 123
+ console.log('this should be kept')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "main": "index-main.js",
+ "module": "index-module.js",
+ "sideEffects": ["./index-module.js"]
+ }
+ `,
+ },
+ });
+ itBundled("dce/PackageJsonSideEffectsArrayKeepModuleImplicitMain", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import {foo} from "demo-pkg"
+ import "./require-demo-pkg"
+ console.log('unused import')
+ `,
+ "/Users/user/project/src/require-demo-pkg.js": /* js */ `
+ // This causes "index-main.js" to be selected
+ require('demo-pkg')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/index-main.js": /* js */ `
+ export const foo = 123
+ console.log('this should be kept')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/index-module.js": /* js */ `
+ export const foo = 123
+ console.log('TEST FAILED')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "main": "index-main.js",
+ "module": "index-module.js",
+ "sideEffects": ["./index-module.js"]
+ }
+ `,
+ },
+ });
+ itBundled("dce/PackageJsonSideEffectsArrayGlob", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import "demo-pkg/keep/this/file"
+ import "demo-pkg/remove/this/file"
+ `,
+ "/Users/user/project/node_modules/demo-pkg/keep/this/file.js": `console.log('this should be kept')`,
+ "/Users/user/project/node_modules/demo-pkg/remove/this/file.js": `console.log('TEST FAILED')`,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "sideEffects": [
+ "./ke?p/*/file.js",
+ "./remove/this/file.j",
+ "./re?ve/this/file.js"
+ ]
+ }
+ `,
+ },
+ /* 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:
+ `, */
+ });
+ itBundled("dce/PackageJsonSideEffectsNestedDirectoryRemove", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import {foo} from "demo-pkg/a/b/c"
+ console.log('unused import')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "sideEffects": false
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/a/b/c/index.js": /* js */ `
+ export const foo = 123
+ console.log('hello')
+ `,
+ },
+ });
+ itBundled("dce/PackageJsonSideEffectsKeepExportDefaultExpr", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import foo from "demo-pkg"
+ console.log(foo)
+ `,
+ "/Users/user/project/node_modules/demo-pkg/index.js": `export default exprWithSideEffects()`,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "sideEffects": false
+ }
+ `,
+ },
+ });
+ itBundled("dce/PackageJsonSideEffectsFalseNoWarningInNodeModulesIssue999", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import "demo-pkg"
+ console.log('used import')
+ `,
+ "/Users/user/project/node_modules/demo-pkg/index.js": /* js */ `
+ import "demo-pkg2"
+ console.log('unused import')
+ `,
+ "/Users/user/project/node_modules/demo-pkg2/index.js": /* js */ `
+ export const foo = 123
+ console.log('hello')
+ `,
+ "/Users/user/project/node_modules/demo-pkg2/package.json": /* json */ `
+ {
+ "sideEffects": false
+ }
+ `,
+ },
+ });
+ 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 */ `
+ export {foo} from "./foo.js"
+ throw 'REMOVE THIS'
+ `,
+ "/Users/user/project/node_modules/demo-pkg/foo.js": `export const foo = 123`,
+ "/Users/user/project/node_modules/demo-pkg/package.json": `{ "sideEffects": false }`,
+ },
+ });
+ itBundled("dce/PackageJsonSideEffectsFalseIntermediateFilesUsed", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import {foo} from "demo-pkg"
+ console.log(foo)
+ `,
+ "/Users/user/project/node_modules/demo-pkg/index.js": /* js */ `
+ export {foo} from "./foo.js"
+ throw 'keep this'
+ `,
+ "/Users/user/project/node_modules/demo-pkg/foo.js": `export const foo = 123`,
+ "/Users/user/project/node_modules/demo-pkg/package.json": `{ "sideEffects": false }`,
+ },
+ });
+ itBundled("dce/PackageJsonSideEffectsFalseIntermediateFilesChainAll", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import {foo} from "a"
+ console.log(foo)
+ `,
+ "/Users/user/project/node_modules/a/index.js": `export {foo} from "b"`,
+ "/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'
+ `,
+ "/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/c/package.json": `{ "sideEffects": false }`,
+ "/Users/user/project/node_modules/d/index.js": `export const foo = 123`,
+ "/Users/user/project/node_modules/d/package.json": `{ "sideEffects": false }`,
+ },
+ });
+ itBundled("dce/PackageJsonSideEffectsFalseIntermediateFilesChainOne", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import {foo} from "a"
+ console.log(foo)
+ `,
+ "/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'
+ `,
+ "/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`,
+ },
+ });
+ itBundled("dce/PackageJsonSideEffectsFalseIntermediateFilesDiamond", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import {foo} from "a"
+ console.log(foo)
+ `,
+ "/Users/user/project/node_modules/a/index.js": /* js */ `
+ export * from "b1"
+ export * from "b2"
+ `,
+ "/Users/user/project/node_modules/b1/index.js": /* js */ `
+ export {foo} from "c"
+ throw 'keep this 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'
+ `,
+ "/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`,
+ },
+ });
+ itBundled("dce/PackageJsonSideEffectsFalseOneFork", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": `import("a").then(x => assert(x.foo === "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"
+ export {baz} from "d"
+ `,
+ "/Users/user/project/node_modules/b/package.json": `{ "sideEffects": false }`,
+ "/Users/user/project/node_modules/c/index.js": /* js */ `
+ export let foo = "foo"
+ export let bar = "bar"
+ `,
+ "/Users/user/project/node_modules/d/index.js": `export let baz = "baz"`,
+ },
+ });
+ itBundled("dce/PackageJsonSideEffectsFalseAllFork", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": `import("a").then(x => assert(x.foo === "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"
+ export {baz} from "d"
+ `,
+ "/Users/user/project/node_modules/b/package.json": `{ "sideEffects": false }`,
+ "/Users/user/project/node_modules/c/index.js": /* js */ `
+ export let foo = "foo"
+ export let bar = "bar"
+ `,
+ "/Users/user/project/node_modules/c/package.json": `{ "sideEffects": false }`,
+ "/Users/user/project/node_modules/d/index.js": `export let baz = "baz"`,
+ "/Users/user/project/node_modules/d/package.json": `{ "sideEffects": false }`,
+ },
+ });
+ itBundled("dce/JSONLoaderRemoveUnused", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import unused from "./example.json"
+ console.log('unused import')
+ `,
+ "/example.json": `{"data": true}`,
+ },
+ });
+ itBundled("dce/TextLoaderRemoveUnused", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import unused from "./example.txt"
+ console.log('unused import')
+ `,
+ "/example.txt": `some data`,
+ },
+ });
+ itBundled("dce/Base64LoaderRemoveUnused", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import unused from "./example.data"
+ console.log('unused import')
+ `,
+ "/example.data": `some data`,
+ },
+ });
+ itBundled("dce/DataURLLoaderRemoveUnused", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import unused from "./example.data"
+ console.log('unused import')
+ `,
+ "/example.data": `some data`,
+ },
+ });
+ itBundled("dce/FileLoaderRemoveUnused", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import unused from "./example.data"
+ console.log('unused import')
+ `,
+ "/example.data": `some data`,
+ },
+ });
+ itBundled("dce/RemoveUnusedImportMeta", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ function foo() {
+ console.log(import.meta.url, import.meta.path)
+ }
+ console.log('foo is unused')
+ `,
+ },
+ });
+ itBundled("dce/RemoveUnusedPureCommentCalls", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ function bar() {}
+ let bare = foo(bar);
+
+ let at_yes = /* @__PURE__ */ foo(bar);
+ let at_no = /* @__PURE__ */ foo(bar());
+ let new_at_yes = /* @__PURE__ */ new foo(bar);
+ let new_at_no = /* @__PURE__ */ new foo(bar());
+
+ let nospace_at_yes = /*@__PURE__*/ foo(bar);
+ let nospace_at_no = /*@__PURE__*/ foo(bar());
+ let nospace_new_at_yes = /*@__PURE__*/ new foo(bar);
+ let nospace_new_at_no = /*@__PURE__*/ new foo(bar());
+
+ let num_yes = /* #__PURE__ */ foo(bar);
+ let num_no = /* #__PURE__ */ foo(bar());
+ let new_num_yes = /* #__PURE__ */ new foo(bar);
+ let new_num_no = /* #__PURE__ */ new foo(bar());
+
+ let nospace_num_yes = /*#__PURE__*/ foo(bar);
+ let nospace_num_no = /*#__PURE__*/ foo(bar());
+ let nospace_new_num_yes = /*#__PURE__*/ new foo(bar);
+ let nospace_new_num_no = /*#__PURE__*/ new foo(bar());
+
+ let dot_yes = /* @__PURE__ */ foo(sideEffect()).dot(bar);
+ let dot_no = /* @__PURE__ */ foo(sideEffect()).dot(bar());
+ let new_dot_yes = /* @__PURE__ */ new foo(sideEffect()).dot(bar);
+ let new_dot_no = /* @__PURE__ */ new foo(sideEffect()).dot(bar());
+
+ let nested_yes = [1, /* @__PURE__ */ foo(bar), 2];
+ let nested_no = [1, /* @__PURE__ */ foo(bar()), 2];
+ let new_nested_yes = [1, /* @__PURE__ */ new foo(bar), 2];
+ let new_nested_no = [1, /* @__PURE__ */ new foo(bar()), 2];
+
+ let single_at_yes = // @__PURE__
+ foo(bar);
+ let single_at_no = // @__PURE__
+ foo(bar());
+ let new_single_at_yes = // @__PURE__
+ new foo(bar);
+ let new_single_at_no = // @__PURE__
+ new foo(bar());
+
+ let single_num_yes = // #__PURE__
+ foo(bar);
+ let single_num_no = // #__PURE__
+ foo(bar());
+ let new_single_num_yes = // #__PURE__
+ new foo(bar);
+ let new_single_num_no = // #__PURE__
+ new foo(bar());
+
+ let bad_no = /* __PURE__ */ foo(bar);
+ let new_bad_no = /* __PURE__ */ new foo(bar);
+
+ let parens_no = (/* @__PURE__ */ foo)(bar);
+ let new_parens_no = new (/* @__PURE__ */ foo)(bar);
+
+ let exp_no = /* @__PURE__ */ foo() ** foo();
+ let new_exp_no = /* @__PURE__ */ new foo() ** foo();
+ `,
+ },
+ });
+ itBundled("dce/TreeShakingReactElements", {
+ // GENERATED
+ files: {
+ "/entry.jsx": /* jsx */ `
+ function Foo() {}
+
+ let a = <div/>
+ let b = <Foo>{a}</Foo>
+ let c = <>{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()
+ `,
+ "/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": "???",
+ },
+ });
+ itBundled("dce/DeadCodeFollowingJump", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ function testReturn() {
+ if (true) return y + z()
+ if (FAIL) return FAIL
+ if (x) { var y }
+ function z() { KEEP_ME() }
+ return FAIL
+ }
+
+ function testThrow() {
+ if (true) throw y + z()
+ if (FAIL) return FAIL
+ if (x) { var y }
+ function z() { KEEP_ME() }
+ return FAIL
+ }
+
+ function testBreak() {
+ while (true) {
+ if (true) {
+ y + z()
+ break
+ }
+ if (FAIL) return FAIL
+ if (x) { var y }
+ function z() { KEEP_ME() }
+ return FAIL
+ }
+ }
+
+ function testContinue() {
+ while (true) {
+ if (true) {
+ y + z()
+ continue
+ }
+ if (FAIL) return FAIL
+ if (x) { var y }
+ function z() { KEEP_ME() }
+ return FAIL
+ }
+ }
+
+ function testStmts() {
+ return [a, b, c, d, e, f, g, h, i]
+
+ while (x) { var a }
+ while (FAIL) { let FAIL }
+
+ do { var b } while (x)
+ do { let FAIL } while (FAIL)
+
+ for (var c; ;) ;
+ for (let FAIL; ;) ;
+
+ for (var d in x) ;
+ for (let FAIL in FAIL) ;
+
+ for (var e of x) ;
+ for (let FAIL of FAIL) ;
+
+ if (x) { var f }
+ if (FAIL) { let FAIL }
+
+ if (x) ; else { var g }
+ if (FAIL) ; else { let FAIL }
+
+ { var h }
+ { let FAIL }
+
+ x: { var i }
+ x: { let FAIL }
+ }
+
+ testReturn()
+ testThrow()
+ testBreak()
+ testContinue()
+ testStmts()
+ `,
+ },
+ });
+ itBundled("dce/RemoveTrailingReturn", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ function foo() {
+ if (a) b()
+ return
+ }
+ function bar() {
+ if (a) b()
+ return KEEP_ME
+ }
+ export default [
+ foo,
+ bar,
+ function () {
+ if (a) b()
+ return
+ },
+ function () {
+ if (a) b()
+ return KEEP_ME
+ },
+ () => {
+ if (a) b()
+ return
+ },
+ () => {
+ if (a) b()
+ return KEEP_ME
+ },
+ ]
+ `,
+ },
+ minifySyntax: true,
+ });
+ itBundled("dce/ImportReExportOfNamespaceImport", {
+ // GENERATED
+ files: {
+ "/Users/user/project/entry.js": /* js */ `
+ import * as ns from 'pkg'
+ console.log(ns.foo)
+ `,
+ "/Users/user/project/node_modules/pkg/index.js": /* js */ `
+ export { default as foo } from './foo'
+ export { default as bar } from './bar'
+ `,
+ "/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'`,
+ },
+ });
+ itBundled("dce/TreeShakingImportIdentifier", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import * as a from './a'
+ new a.Keep()
+ `,
+ "/a.js": /* js */ `
+ import * as b from './b'
+ export class Keep extends b.Base {}
+ export class REMOVE extends b.Base {}
+ `,
+ "/b.js": `export class Base {}`,
+ },
+ });
+ itBundled("dce/TreeShakingObjectProperty", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ let remove1 = { x: 'x' }
+ let remove2 = { x() {} }
+ let remove3 = { get x() {} }
+ let remove4 = { set x(_) {} }
+ let remove5 = { async x() {} }
+ let remove6 = { ['x']: 'x' }
+ let remove7 = { ['x']() {} }
+ let remove8 = { get ['x']() {} }
+ let remove9 = { set ['x'](_) {} }
+ let remove10 = { async ['x']() {} }
+ let remove11 = { [0]: 'x' }
+ let remove12 = { [null]: 'x' }
+ let remove13 = { [undefined]: 'x' }
+ let remove14 = { [false]: 'x' }
+ let remove15 = { [0n]: 'x' }
+ let remove16 = { toString() {} }
+
+ let keep1 = { x }
+ let keep2 = { x: x }
+ let keep3 = { ...x }
+ let keep4 = { [x]: 'x' }
+ let keep5 = { [x]() {} }
+ let keep6 = { get [x]() {} }
+ let keep7 = { set [x](_) {} }
+ let keep8 = { async [x]() {} }
+ let keep9 = { [{ toString() {} }]: 'x' }
+ `,
+ },
+ treeShaking: true,
+ mode: "passthrough",
+ });
+ itBundled("dce/TreeShakingClassProperty", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ let remove1 = class { x }
+ let remove2 = class { x = x }
+ let remove3 = class { x() {} }
+ let remove4 = class { get x() {} }
+ let remove5 = class { set x(_) {} }
+ let remove6 = class { async x() {} }
+ let remove7 = class { ['x'] = x }
+ let remove8 = class { ['x']() {} }
+ let remove9 = class { get ['x']() {} }
+ let remove10 = class { set ['x'](_) {} }
+ let remove11 = class { async ['x']() {} }
+ let remove12 = class { [0] = 'x' }
+ let remove13 = class { [null] = 'x' }
+ let remove14 = class { [undefined] = 'x' }
+ 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' }
+ `,
+ },
+ treeShaking: true,
+ mode: "passthrough",
+ });
+ itBundled("dce/TreeShakingClassStaticProperty", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ let remove1 = class { static x }
+ let remove3 = class { static x() {} }
+ let remove4 = class { static get x() {} }
+ let remove5 = class { static set x(_) {} }
+ let remove6 = class { static async x() {} }
+ let remove8 = class { static ['x']() {} }
+ let remove9 = class { static get ['x']() {} }
+ let remove10 = class { static set ['x'](_) {} }
+ let remove11 = class { static async ['x']() {} }
+ let remove12 = class { static [0] = 'x' }
+ let remove13 = class { static [null] = 'x' }
+ let remove14 = class { static [undefined] = 'x' }
+ let remove15 = class { static [false] = 'x' }
+ let remove16 = class { static [0n] = 'x' }
+ let remove17 = class { static toString() {} }
+
+ let keep1 = class { static x = x }
+ let keep2 = class { static ['x'] = x }
+ let keep3 = class { static [x] = 'x' }
+ let keep4 = class { static [x]() {} }
+ let keep5 = class { static get [x]() {} }
+ let keep6 = class { static set [x](_) {} }
+ let keep7 = class { static async [x]() {} }
+ let keep8 = class { static [{ toString() {} }] = 'x' }
+ `,
+ },
+ treeShaking: true,
+ mode: "passthrough",
+ });
+ itBundled("dce/TreeShakingUnaryOperators", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ // These operators may have side effects
+ let keep;
+ +keep;
+ -keep;
+ ~keep;
+ delete keep;
+ ++keep;
+ --keep;
+ keep++;
+ keep--;
+
+ // These operators never have side effects
+ let REMOVE;
+ !REMOVE;
+ void REMOVE;
+ `,
+ },
+ });
+ itBundled("dce/TreeShakingBinaryOperators", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ // These operators may have side effects
+ let keep, keep2;
+ keep + keep2;
+ keep - keep2;
+ keep * keep2;
+ keep / keep2;
+ keep % keep2;
+ keep ** keep2;
+ keep < keep2;
+ keep <= keep2;
+ keep > keep2;
+ keep >= keep2;
+ keep in keep2;
+ keep instanceof keep2;
+ keep << keep2;
+ keep >> keep2;
+ keep >>> keep2;
+ keep == keep2;
+ keep != keep2;
+ keep | keep2;
+ keep & keep2;
+ keep ^ keep2;
+ keep = keep2;
+ keep += keep2;
+ keep -= keep2;
+ keep *= keep2;
+ keep /= keep2;
+ keep %= keep2;
+ keep **= keep2;
+ keep <<= keep2;
+ keep >>= keep2;
+ keep >>>= keep2;
+ keep |= keep2;
+ keep &= keep2;
+ keep ^= keep2;
+ keep ??= keep2;
+ keep ||= keep2;
+ keep &&= keep2;
+
+ // These operators never have side effects
+ let REMOVE, REMOVE2;
+ REMOVE === REMOVE2;
+ REMOVE !== REMOVE2;
+ REMOVE, REMOVE2;
+ REMOVE ?? REMOVE2;
+ REMOVE || REMOVE2;
+ REMOVE && REMOVE2;
+ `,
+ },
+ });
+ itBundled("dce/TreeShakingNoBundleESM", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ function keep() {}
+ function unused() {}
+ keep()
+ `,
+ },
+ format: "esm",
+ mode: "convertformat",
+ });
+ itBundled("dce/TreeShakingNoBundleCJS", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ function keep() {}
+ function unused() {}
+ keep()
+ `,
+ },
+ format: "cjs",
+ mode: "convertformat",
+ });
+ itBundled("dce/TreeShakingNoBundleIIFE", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ function keep() {}
+ function REMOVE() {}
+ keep()
+ `,
+ },
+ format: "iife",
+ mode: "convertformat",
+ });
+ itBundled("dce/TreeShakingInESMWrapper", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import {keep1} from './lib'
+ console.log(keep1(), require('./cjs'))
+ `,
+ "/cjs.js": /* js */ `
+ import {keep2} from './lib'
+ export default keep2()
+ `,
+ "/lib.js": /* js */ `
+ export let keep1 = () => 'keep1'
+ export let keep2 = () => 'keep2'
+ export let REMOVE = () => 'REMOVE'
+ `,
+ },
+ format: "esm",
+ });
+ itBundled("dce/DCETypeOf", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ // These should be removed because they have no side effects
+ typeof x_REMOVE
+ typeof v_REMOVE
+ typeof f_REMOVE
+ typeof g_REMOVE
+ typeof a_REMOVE
+ var v_REMOVE
+ function f_REMOVE() {}
+ function* g_REMOVE() {}
+ async function a_REMOVE() {}
+
+ // These technically have side effects due to TDZ, but this is not currently handled
+ typeof c_remove
+ typeof l_remove
+ typeof s_remove
+ const c_remove = 0
+ let l_remove
+ class s_remove {}
+ `,
+ },
+ format: "esm",
+ });
+ itBundled("dce/DCETypeOfEqualsString", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ var hasBar = typeof bar !== 'undefined'
+ if (false) console.log(hasBar)
+ `,
+ },
+ format: "iife",
+ });
+ 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)
+ `,
+ },
+ format: "iife",
+ minifySyntax: true,
+ });
+ itBundled("dce/DCETypeOfEqualsStringGuardCondition", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ // Everything here should be removed as dead code due to tree shaking
+ var REMOVE_1 = typeof x !== 'undefined' ? x : null
+ var REMOVE_1 = typeof x != 'undefined' ? x : null
+ var REMOVE_1 = typeof x === 'undefined' ? null : x
+ var REMOVE_1 = typeof x == 'undefined' ? null : x
+ var REMOVE_1 = typeof x !== 'undefined' && x
+ var REMOVE_1 = typeof x != 'undefined' && x
+ var REMOVE_1 = typeof x === 'undefined' || x
+ var REMOVE_1 = typeof x == 'undefined' || x
+ var REMOVE_1 = 'undefined' !== typeof x ? x : null
+ var REMOVE_1 = 'undefined' != typeof x ? x : null
+ var REMOVE_1 = 'undefined' === typeof x ? null : x
+ var REMOVE_1 = 'undefined' == typeof x ? null : x
+ var REMOVE_1 = 'undefined' !== typeof x && x
+ var REMOVE_1 = 'undefined' != typeof x && x
+ var REMOVE_1 = 'undefined' === typeof x || x
+ var REMOVE_1 = 'undefined' == typeof x || x
+
+ // Everything here should be removed as dead code due to tree shaking
+ var REMOVE_2 = typeof x === 'object' ? x : null
+ var REMOVE_2 = typeof x == 'object' ? x : null
+ var REMOVE_2 = typeof x !== 'object' ? null : x
+ var REMOVE_2 = typeof x != 'object' ? null : x
+ var REMOVE_2 = typeof x === 'object' && x
+ var REMOVE_2 = typeof x == 'object' && x
+ var REMOVE_2 = typeof x !== 'object' || x
+ var REMOVE_2 = typeof x != 'object' || x
+ var REMOVE_2 = 'object' === typeof x ? x : null
+ var REMOVE_2 = 'object' == typeof x ? x : null
+ var REMOVE_2 = 'object' !== typeof x ? null : x
+ var REMOVE_2 = 'object' != typeof x ? null : x
+ var REMOVE_2 = 'object' === typeof x && x
+ var REMOVE_2 = 'object' == typeof x && x
+ var REMOVE_2 = 'object' !== typeof x || x
+ var REMOVE_2 = 'object' != typeof x || x
+
+ // Everything here should be kept as live code because it has side effects
+ var keep_1 = typeof x !== 'object' ? x : null
+ var keep_1 = typeof x != 'object' ? x : null
+ var keep_1 = typeof x === 'object' ? null : x
+ var keep_1 = typeof x == 'object' ? null : x
+ var keep_1 = typeof x !== 'object' && x
+ var keep_1 = typeof x != 'object' && x
+ var keep_1 = typeof x === 'object' || x
+ var keep_1 = typeof x == 'object' || x
+ var keep_1 = 'object' !== typeof x ? x : null
+ var keep_1 = 'object' != typeof x ? x : null
+ var keep_1 = 'object' === typeof x ? null : x
+ var keep_1 = 'object' == typeof x ? null : x
+ var keep_1 = 'object' !== typeof x && x
+ var keep_1 = 'object' != typeof x && x
+ var keep_1 = 'object' === typeof x || x
+ var keep_1 = 'object' == typeof x || x
+
+ // Everything here should be kept as live code because it has side effects
+ var keep_2 = typeof x !== 'undefined' ? y : null
+ var keep_2 = typeof x != 'undefined' ? y : null
+ var keep_2 = typeof x === 'undefined' ? null : y
+ var keep_2 = typeof x == 'undefined' ? null : y
+ var keep_2 = typeof x !== 'undefined' && y
+ var keep_2 = typeof x != 'undefined' && y
+ var keep_2 = typeof x === 'undefined' || y
+ var keep_2 = typeof x == 'undefined' || y
+ var keep_2 = 'undefined' !== typeof x ? y : null
+ var keep_2 = 'undefined' != typeof x ? y : null
+ var keep_2 = 'undefined' === typeof x ? null : y
+ var keep_2 = 'undefined' == typeof x ? null : y
+ var keep_2 = 'undefined' !== typeof x && y
+ var keep_2 = 'undefined' != typeof x && y
+ var keep_2 = 'undefined' === typeof x || y
+ var keep_2 = 'undefined' == typeof x || y
+
+ // Everything here should be kept as live code because it has side effects
+ var keep_3 = typeof x !== 'undefined' ? null : x
+ var keep_3 = typeof x != 'undefined' ? null : x
+ var keep_3 = typeof x === 'undefined' ? x : null
+ var keep_3 = typeof x == 'undefined' ? x : null
+ var keep_3 = typeof x !== 'undefined' || x
+ var keep_3 = typeof x != 'undefined' || x
+ var keep_3 = typeof x === 'undefined' && x
+ var keep_3 = typeof x == 'undefined' && x
+ var keep_3 = 'undefined' !== typeof x ? null : x
+ var keep_3 = 'undefined' != typeof x ? null : x
+ var keep_3 = 'undefined' === typeof x ? x : null
+ var keep_3 = 'undefined' == typeof x ? x : null
+ var keep_3 = 'undefined' !== typeof x || x
+ var keep_3 = 'undefined' != typeof x || x
+ var keep_3 = 'undefined' === typeof x && x
+ var keep_3 = 'undefined' == typeof x && x
+ `,
+ },
+ format: "iife",
+ });
+ itBundled("dce/DCETypeOfCompareStringGuardCondition", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ // Everything here should be removed as dead code due to tree shaking
+ var REMOVE_1 = typeof x <= 'u' ? x : null
+ var REMOVE_1 = typeof x < 'u' ? x : null
+ var REMOVE_1 = typeof x >= 'u' ? null : x
+ var REMOVE_1 = typeof x > 'u' ? null : x
+ var REMOVE_1 = typeof x <= 'u' && x
+ var REMOVE_1 = typeof x < 'u' && x
+ var REMOVE_1 = typeof x >= 'u' || x
+ var REMOVE_1 = typeof x > 'u' || x
+ var REMOVE_1 = 'u' >= typeof x ? x : null
+ var REMOVE_1 = 'u' > typeof x ? x : null
+ var REMOVE_1 = 'u' <= typeof x ? null : x
+ var REMOVE_1 = 'u' < typeof x ? null : x
+ var REMOVE_1 = 'u' >= typeof x && x
+ var REMOVE_1 = 'u' > typeof x && x
+ var REMOVE_1 = 'u' <= typeof x || x
+ var REMOVE_1 = 'u' < typeof x || x
+
+ // Everything here should be kept as live code because it has side effects
+ var keep_1 = typeof x <= 'u' ? y : null
+ var keep_1 = typeof x < 'u' ? y : null
+ var keep_1 = typeof x >= 'u' ? null : y
+ var keep_1 = typeof x > 'u' ? null : y
+ var keep_1 = typeof x <= 'u' && y
+ var keep_1 = typeof x < 'u' && y
+ var keep_1 = typeof x >= 'u' || y
+ var keep_1 = typeof x > 'u' || y
+ var keep_1 = 'u' >= typeof x ? y : null
+ var keep_1 = 'u' > typeof x ? y : null
+ var keep_1 = 'u' <= typeof x ? null : y
+ var keep_1 = 'u' < typeof x ? null : y
+ var keep_1 = 'u' >= typeof x && y
+ var keep_1 = 'u' > typeof x && y
+ var keep_1 = 'u' <= typeof x || y
+ var keep_1 = 'u' < typeof x || y
+
+ // Everything here should be kept as live code because it has side effects
+ var keep_2 = typeof x <= 'u' ? null : x
+ var keep_2 = typeof x < 'u' ? null : x
+ var keep_2 = typeof x >= 'u' ? x : null
+ var keep_2 = typeof x > 'u' ? x : null
+ var keep_2 = typeof x <= 'u' || x
+ var keep_2 = typeof x < 'u' || x
+ var keep_2 = typeof x >= 'u' && x
+ var keep_2 = typeof x > 'u' && x
+ var keep_2 = 'u' >= typeof x ? null : x
+ var keep_2 = 'u' > typeof x ? null : x
+ var keep_2 = 'u' <= typeof x ? x : null
+ var keep_2 = 'u' < typeof x ? x : null
+ var keep_2 = 'u' >= typeof x || x
+ var keep_2 = 'u' > typeof x || x
+ var keep_2 = 'u' <= typeof x && x
+ var keep_2 = 'u' < typeof x && x
+ `,
+ },
+ format: "iife",
+ });
+ itBundled("dce/RemoveUnusedImports", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import a from 'a'
+ import * as b from 'b'
+ import {c} from 'c'
+ `,
+ },
+ minifySyntax: true,
+ mode: "passthrough",
+ });
+ 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)')
+ `,
+ },
+ minifySyntax: true,
+ mode: "passthrough",
+ });
+ itBundled("dce/RemoveUnusedImportsEvalTS", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import a from 'a'
+ import * as b from 'b'
+ import {c} from 'c'
+ eval('foo(a, b, c)')
+ `,
+ },
+ entryPoints: ["/entry.js"],
+ minifySyntax: true,
+ mode: "passthrough",
+ });
+ itBundled("dce/DCEClassStaticBlocks", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ class A_REMOVE {
+ static {}
+ }
+ class B_REMOVE {
+ static { 123 }
+ }
+ class C_REMOVE {
+ static { /* @__PURE__*/ foo() }
+ }
+ class D_REMOVE {
+ static { try {} catch {} }
+ }
+ class E_REMOVE {
+ static { try { /* @__PURE__*/ foo() } catch {} }
+ }
+ class F_REMOVE {
+ static { try { 123 } catch { 123 } finally { 123 } }
+ }
+
+ class A_keep {
+ static { foo }
+ }
+ class B_keep {
+ static { this.foo }
+ }
+ class C_keep {
+ static { try { foo } catch {} }
+ }
+ class D_keep {
+ static { try {} finally { foo } }
+ }
+ `,
+ },
+ entryPoints: ["/entry.js"],
+ });
+ itBundled("dce/DCEVarExports", {
+ // GENERATED
+ files: {
+ "/a.js": /* js */ `
+ var foo = { bar: 123 }
+ module.exports = foo
+ `,
+ "/b.js": /* js */ `
+ var exports = { bar: 123 }
+ module.exports = exports
+ `,
+ "/c.js": /* js */ `
+ var module = { bar: 123 }
+ exports.foo = module
+ `,
+ },
+ entryPoints: ["/a.js", "/b.js", "/c.js"],
+ });
+ itBundled("dce/DCETemplateLiteral", {
+ // GENERATED
+ files: {},
+ entryPoints: ["/entry.js"],
+ });
+ itBundled("dce/TreeShakingLoweredClassStaticField", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class REMOVE_ME {
+ static x = 'x'
+ static y = 'y'
+ static z = 'z'
+ }
+ function REMOVE_ME_TOO() {
+ new REMOVE_ME()
+ }
+ class KeepMe1 {
+ static x = 'x'
+ static y = sideEffects()
+ static z = 'z'
+ }
+ class KeepMe2 {
+ static x = 'x'
+ static y = 'y'
+ static z = 'z'
+ }
+ new KeepMe2()
+ `,
+ },
+ });
+ itBundled("dce/TreeShakingLoweredClassStaticFieldMinified", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class REMOVE_ME {
+ static x = 'x'
+ static y = 'y'
+ static z = 'z'
+ }
+ function REMOVE_ME_TOO() {
+ new REMOVE_ME()
+ }
+ class KeepMe1 {
+ static x = 'x'
+ static y = sideEffects()
+ static z = 'z'
+ }
+ class KeepMe2 {
+ static x = 'x'
+ static y = 'y'
+ static z = 'z'
+ }
+ new KeepMe2()
+ `,
+ },
+ unsupportedJSFeatures: "ClassField",
+ });
+ itBundled("dce/TreeShakingLoweredClassStaticFieldAssignment", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ class KeepMe1 {
+ static x = 'x'
+ static y = 'y'
+ static z = 'z'
+ }
+ class KeepMe2 {
+ static x = 'x'
+ static y = sideEffects()
+ static z = 'z'
+ }
+ class KeepMe3 {
+ static x = 'x'
+ static y = 'y'
+ static z = 'z'
+ }
+ new KeepMe3()
+ `,
+ },
+ entryPoints: ["/entry.js"],
+ unsupportedJSFeatures: "ClassField",
+ });
+ itBundled("dce/InlineIdentityFunctionCalls", {
+ // GENERATED
+ files: {
+ "/identity.js": /* js */ `
+ function DROP(x) { return x }
+ console.log(DROP(1))
+ DROP(foo())
+ DROP(1)
+ `,
+ "/identity-last.js": /* js */ `
+ function DROP(x) { return [x] }
+ function DROP(x) { return x }
+ console.log(DROP(1))
+ DROP(foo())
+ DROP(1)
+ `,
+ "/identity-cross-module.js": /* js */ `
+ import { DROP } from './identity-cross-module-def'
+ console.log(DROP(1))
+ DROP(foo())
+ DROP(1)
+ `,
+ "/identity-cross-module-def.js": `export function DROP(x) { return x }`,
+ "/identity-no-args.js": /* js */ `
+ function keep(x) { return x }
+ console.log(keep())
+ keep()
+ `,
+ "/identity-two-args.js": /* js */ `
+ function keep(x) { return x }
+ console.log(keep(1, 2))
+ keep(1, 2)
+ `,
+ "/identity-first.js": /* js */ `
+ function keep(x) { return x }
+ function keep(x) { return [x] }
+ console.log(keep(1))
+ keep(foo())
+ keep(1)
+ `,
+ "/identity-generator.js": /* js */ `
+ function* keep(x) { return x }
+ console.log(keep(1))
+ keep(foo())
+ keep(1)
+ `,
+ "/identity-async.js": /* js */ `
+ async function keep(x) { return x }
+ console.log(keep(1))
+ keep(foo())
+ keep(1)
+ `,
+ "/reassign.js": /* js */ `
+ function keep(x) { return x }
+ keep = reassigned
+ console.log(keep(1))
+ keep(foo())
+ keep(1)
+ `,
+ "/reassign-inc.js": /* js */ `
+ function keep(x) { return x }
+ keep++
+ console.log(keep(1))
+ keep(foo())
+ keep(1)
+ `,
+ "/reassign-div.js": /* js */ `
+ function keep(x) { return x }
+ keep /= reassigned
+ console.log(keep(1))
+ keep(foo())
+ keep(1)
+ `,
+ "/reassign-array.js": /* js */ `
+ function keep(x) { return x }
+ [keep] = reassigned
+ console.log(keep(1))
+ keep(foo())
+ keep(1)
+ `,
+ "/reassign-object.js": /* js */ `
+ function keep(x) { return x }
+ ({keep} = reassigned)
+ console.log(keep(1))
+ keep(foo())
+ keep(1)
+ `,
+ "/not-identity-two-args.js": /* js */ `
+ function keep(x, y) { return x }
+ console.log(keep(1))
+ keep(foo())
+ keep(1)
+ `,
+ "/not-identity-default.js": /* js */ `
+ function keep(x = foo()) { return x }
+ console.log(keep(1))
+ keep(foo())
+ keep(1)
+ `,
+ "/not-identity-array.js": /* js */ `
+ function keep([x]) { return x }
+ console.log(keep(1))
+ keep(foo())
+ keep(1)
+ `,
+ "/not-identity-object.js": /* js */ `
+ function keep({x}) { return x }
+ console.log(keep(1))
+ keep(foo())
+ keep(1)
+ `,
+ "/not-identity-rest.js": /* js */ `
+ function keep(...x) { return x }
+ console.log(keep(1))
+ keep(foo())
+ keep(1)
+ `,
+ "/not-identity-return.js": /* js */ `
+ function keep(x) { return [x] }
+ console.log(keep(1))
+ keep(foo())
+ keep(1)
+ `,
+ },
+ entryPoints: [
+ "/identity.js",
+ "/identity-last.js",
+ "/identity-first.js",
+ "/identity-generator.js",
+ "/identity-async.js",
+ "/identity-cross-module.js",
+ "/identity-no-args.js",
+ "/identity-two-args.js",
+ "/reassign.js",
+ "/reassign-inc.js",
+ "/reassign-div.js",
+ "/reassign-array.js",
+ "/reassign-object.js",
+ "/not-identity-two-args.js",
+ "/not-identity-default.js",
+ "/not-identity-array.js",
+ "/not-identity-object.js",
+ "/not-identity-rest.js",
+ "/not-identity-return.js",
+ ],
+ });
+ itBundled("dce/InlineEmptyFunctionCalls", {
+ // GENERATED
+ files: {
+ "/empty.js": /* js */ `
+ function DROP() {}
+ console.log(DROP(foo(), bar()))
+ console.log(DROP(foo(), 1))
+ console.log(DROP(1, foo()))
+ console.log(DROP(1))
+ console.log(DROP())
+ DROP(foo(), bar())
+ DROP(foo(), 1)
+ DROP(1, foo())
+ DROP(1)
+ DROP()
+ `,
+ "/empty-comma.js": /* js */ `
+ function DROP() {}
+ console.log((DROP(), DROP(), foo()))
+ console.log((DROP(), foo(), DROP()))
+ console.log((foo(), DROP(), DROP()))
+ for (DROP(); DROP(); DROP()) DROP();
+ DROP(), DROP(), foo();
+ DROP(), foo(), DROP();
+ foo(), DROP(), DROP();
+ `,
+ "/empty-if-else.js": /* js */ `
+ function DROP() {}
+ if (foo) { let bar = baz(); bar(); bar() } else DROP();
+ `,
+ "/empty-last.js": /* js */ `
+ function DROP() { return x }
+ function DROP() { return }
+ console.log(DROP())
+ DROP()
+ `,
+ "/empty-cross-module.js": /* js */ `
+ import { DROP } from './empty-cross-module-def'
+ console.log(DROP())
+ DROP()
+ `,
+ "/empty-cross-module-def.js": `export function DROP() {}`,
+ "/empty-first.js": /* js */ `
+ function keep() { return }
+ function keep() { return x }
+ console.log(keep())
+ keep(foo())
+ keep(1)
+ `,
+ "/empty-generator.js": /* js */ `
+ function* keep() {}
+ console.log(keep())
+ keep(foo())
+ keep(1)
+ `,
+ "/empty-async.js": /* js */ `
+ async function keep() {}
+ console.log(keep())
+ keep(foo())
+ keep(1)
+ `,
+ "/reassign.js": /* js */ `
+ function keep() {}
+ keep = reassigned
+ console.log(keep())
+ keep(foo())
+ keep(1)
+ `,
+ "/reassign-inc.js": /* js */ `
+ function keep() {}
+ keep++
+ console.log(keep(1))
+ keep(foo())
+ keep(1)
+ `,
+ "/reassign-div.js": /* js */ `
+ function keep() {}
+ keep /= reassigned
+ console.log(keep(1))
+ keep(foo())
+ keep(1)
+ `,
+ "/reassign-array.js": /* js */ `
+ function keep() {}
+ [keep] = reassigned
+ console.log(keep(1))
+ keep(foo())
+ keep(1)
+ `,
+ "/reassign-object.js": /* js */ `
+ function keep() {}
+ ({keep} = reassigned)
+ console.log(keep(1))
+ keep(foo())
+ keep(1)
+ `,
+ },
+ entryPoints: [
+ "/empty.js",
+ "/empty-comma.js",
+ "/empty-if-else.js",
+ "/empty-last.js",
+ "/empty-cross-module.js",
+ "/empty-first.js",
+ "/empty-generator.js",
+ "/empty-async.js",
+ "/reassign.js",
+ "/reassign-inc.js",
+ "/reassign-div.js",
+ "/reassign-array.js",
+ "/reassign-object.js",
+ ],
+ });
+ 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",
+ });
+ itBundled("dce/InlineFunctionCallForInitDecl", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ function empty() {}
+ function id(x) { return x }
+
+ for (var y = empty(); false; ) ;
+ for (var z = id(123); false; ) ;
+ `,
+ },
+ });
+ itBundled("dce/ConstValueInliningNoBundle", {
+ // GENERATED
+ files: {
+ "/top-level.js": /* js */ `
+ // These should be kept because they are top-level and tree shaking is not enabled
+ const n_keep = null
+ const u_keep = undefined
+ const i_keep = 1234567
+ const f_keep = 123.456
+ const s_keep = ''
+
+ // Values should still be inlined
+ console.log(
+ // These are doubled to avoid the "inline const/let into next statement if used once" optimization
+ n_keep, n_keep,
+ u_keep, u_keep,
+ i_keep, i_keep,
+ f_keep, f_keep,
+ s_keep, s_keep,
+ )
+ `,
+ "/nested-block.js": /* js */ `
+ {
+ const REMOVE_n = null
+ const REMOVE_u = undefined
+ const REMOVE_i = 1234567
+ const REMOVE_f = 123.456
+ const s_keep = '' // String inlining is intentionally not supported right now
+ console.log(
+ // These are doubled to avoid the "inline const/let into next statement if used once" optimization
+ REMOVE_n, REMOVE_n,
+ REMOVE_u, REMOVE_u,
+ REMOVE_i, REMOVE_i,
+ REMOVE_f, REMOVE_f,
+ s_keep, s_keep,
+ )
+ }
+ `,
+ "/nested-function.js": /* js */ `
+ function nested() {
+ const REMOVE_n = null
+ const REMOVE_u = undefined
+ const REMOVE_i = 1234567
+ const REMOVE_f = 123.456
+ const s_keep = '' // String inlining is intentionally not supported right now
+ console.log(
+ // These are doubled to avoid the "inline const/let into next statement if used once" optimization
+ REMOVE_n, REMOVE_n,
+ REMOVE_u, REMOVE_u,
+ REMOVE_i, REMOVE_i,
+ REMOVE_f, REMOVE_f,
+ s_keep, s_keep,
+ )
+ }
+ `,
+ "/namespace-export.ts": /* ts */ `
+ namespace ns {
+ const x_REMOVE = 1
+ export const y_keep = 2
+ console.log(
+ x_REMOVE, x_REMOVE,
+ y_keep, y_keep,
+ )
+ }
+ `,
+ "/comment-before.js": /* js */ `
+ {
+ //! comment
+ const REMOVE = 1
+ x = [REMOVE, REMOVE]
+ }
+ `,
+ "/directive-before.js": /* js */ `
+ function nested() {
+ 'directive'
+ const REMOVE = 1
+ x = [REMOVE, REMOVE]
+ }
+ `,
+ "/semicolon-before.js": /* js */ `
+ {
+ ;
+ const REMOVE = 1
+ x = [REMOVE, REMOVE]
+ }
+ `,
+ "/debugger-before.js": /* js */ `
+ {
+ debugger
+ const REMOVE = 1
+ x = [REMOVE, REMOVE]
+ }
+ `,
+ "/type-before.ts": /* ts */ `
+ {
+ declare let x
+ const REMOVE = 1
+ x = [REMOVE, REMOVE]
+ }
+ `,
+ "/exprs-before.js": /* js */ `
+ function nested() {
+ const x = [, '', {}, 0n, /./, function() {}, () => {}]
+ const y_REMOVE = 1
+ function foo() {
+ return y_REMOVE
+ }
+ }
+ `,
+ "/disabled-tdz.js": /* js */ `
+ foo()
+ const x_keep = 1
+ function foo() {
+ return x_keep
+ }
+ `,
+ "/backwards-reference-top-level.js": /* js */ `
+ const x = y
+ const y = 1
+ console.log(
+ x, x,
+ y, y,
+ )
+ `,
+ "/backwards-reference-nested-function.js": /* js */ `
+ function foo() {
+ const x = y
+ const y = 1
+ console.log(
+ x, x,
+ y, y,
+ )
+ }
+ `,
+ },
+ entryPoints: [
+ "/top-level.js",
+ "/nested-block.js",
+ "/nested-function.js",
+ "/namespace-export.ts",
+ "/comment-before.js",
+ "/directive-before.js",
+ "/semicolon-before.js",
+ "/debugger-before.js",
+ "/type-before.ts",
+ "/exprs-before.js",
+ "/disabled-tdz.js",
+ "/backwards-reference-top-level.js",
+ "/backwards-reference-nested-function.js",
+ ],
+ mode: "passthrough",
+ });
+ itBundled("dce/ConstValueInliningBundle", {
+ // GENERATED
+ files: {
+ "/exported-entry.js": /* js */ `
+ const x_REMOVE = 1
+ export const y_keep = 2
+ console.log(
+ x_REMOVE,
+ y_keep,
+ )
+ `,
+ "/re-exported-entry.js": /* js */ `
+ import { x_REMOVE, y_keep } from './re-exported-constants'
+ console.log(x_REMOVE, y_keep)
+ export { y_keep }
+ `,
+ "/re-exported-constants.js": /* js */ `
+ export const x_REMOVE = 1
+ export const y_keep = 2
+ `,
+ "/re-exported-2-entry.js": `export { y_keep } from './re-exported-2-constants'`,
+ "/re-exported-2-constants.js": /* js */ `
+ export const x_REMOVE = 1
+ export const y_keep = 2
+ `,
+ "/re-exported-star-entry.js": `export * from './re-exported-star-constants'`,
+ "/re-exported-star-constants.js": /* js */ `
+ export const x_keep = 1
+ export const y_keep = 2
+ `,
+ "/cross-module-entry.js": /* js */ `
+ import { x_REMOVE, y_keep } from './cross-module-constants'
+ console.log(x_REMOVE, y_keep)
+ `,
+ "/cross-module-constants.js": /* js */ `
+ export const x_REMOVE = 1
+ foo()
+ export const y_keep = 1
+ export function foo() {
+ return [x_REMOVE, y_keep]
+ }
+ `,
+ "/print-shorthand-entry.js": /* js */ `
+ import { foo, _bar } from './print-shorthand-constants'
+ // The inlined constants must still be present in the output! We don't
+ // want the printer to use the shorthand syntax here to refer to the
+ // name of the constant itself because the constant declaration is omitted.
+ console.log({ foo, _bar })
+ `,
+ "/print-shorthand-constants.js": /* js */ `
+ export const foo = 123
+ export const _bar = -321
+ `,
+ "/circular-import-entry.js": `import './circular-import-constants'`,
+ "/circular-import-constants.js": /* js */ `
+ export const foo = 123 // Inlining should be prevented by the cycle
+ export function bar() {
+ return foo
+ }
+ import './circular-import-cycle'
+ `,
+ "/circular-import-cycle.js": /* js */ `
+ import { bar } from './circular-import-constants'
+ console.log(bar()) // This accesses "foo" before it's initialized
+ `,
+ "/circular-re-export-entry.js": /* js */ `
+ import { baz } from './circular-re-export-constants'
+ console.log(baz)
+ `,
+ "/circular-re-export-constants.js": /* js */ `
+ export const foo = 123 // Inlining should be prevented by the cycle
+ export function bar() {
+ return foo
+ }
+ export { baz } from './circular-re-export-cycle'
+ `,
+ "/circular-re-export-cycle.js": /* js */ `
+ export const baz = 0
+ import { bar } from './circular-re-export-constants'
+ console.log(bar()) // This accesses "foo" before it's initialized
+ `,
+ "/circular-re-export-star-entry.js": `import './circular-re-export-star-constants'`,
+ "/circular-re-export-star-constants.js": /* js */ `
+ export const foo = 123 // Inlining should be prevented by the cycle
+ export function bar() {
+ return foo
+ }
+ export * from './circular-re-export-star-cycle'
+ `,
+ "/circular-re-export-star-cycle.js": /* js */ `
+ import { bar } from './circular-re-export-star-constants'
+ console.log(bar()) // This accesses "foo" before it's initialized
+ `,
+ "/non-circular-export-entry.js": /* js */ `
+ import { foo, bar } from './non-circular-export-constants'
+ console.log(foo, bar())
+ `,
+ "/non-circular-export-constants.js": /* js */ `
+ const foo = 123 // Inlining should be prevented by the cycle
+ function bar() {
+ return foo
+ }
+ export { foo, bar }
+ `,
+ },
+ entryPoints: [
+ "/exported-entry.js",
+ "/re-exported-entry.js",
+ "/re-exported-2-entry.js",
+ "/re-exported-star-entry.js",
+ "/cross-module-entry.js",
+ "/print-shorthand-entry.js",
+ "/circular-import-entry.js",
+ "/circular-re-export-entry.js",
+ "/circular-re-export-star-entry.js",
+ "/non-circular-export-entry.js",
+ ],
+ format: "esm",
+ minifySyntax: true,
+ });
+ itBundled("dce/ConstValueInliningAssign", {
+ // GENERATED
+ files: {
+ "/const-assign.js": /* js */ `
+ const x = 1
+ x = 2
+ `,
+ "/const-update.js": /* js */ `
+ const x = 1
+ x += 2
+ `,
+ },
+ 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:
+ `, */
+ });
+ itBundled("dce/ConstValueInliningDirectEval", {
+ // GENERATED
+ files: {
+ "/top-level-no-eval.js": /* js */ `
+ const x = 1
+ console.log(x, evil('x'))
+ `,
+ "/top-level-eval.js": /* js */ `
+ const x = 1
+ console.log(x, eval('x'))
+ `,
+ "/nested-no-eval.js": /* js */ `
+ (() => {
+ const x = 1
+ console.log(x, evil('x'))
+ })()
+ `,
+ "/nested-eval.js": /* js */ `
+ (() => {
+ const x = 1
+ console.log(x, eval('x'))
+ })()
+ `,
+ "/ts-namespace-no-eval.ts": /* ts */ `
+ namespace y {
+ export const x = 1
+ console.log(x, evil('x'))
+ }
+ `,
+ "/ts-namespace-eval.ts": /* ts */ `
+ namespace z {
+ export const x = 1
+ console.log(x, eval('x'))
+ }
+ `,
+ },
+ entryPoints: [
+ "/top-level-no-eval.js",
+ "/top-level-eval.js",
+ "/nested-no-eval.js",
+ "/nested-eval.js",
+ "/ts-namespace-no-eval.ts",
+ "/ts-namespace-eval.ts",
+ ],
+ mode: "passthrough",
+ });
+ itBundled("dce/CrossModuleConstantFolding", {
+ // GENERATED
+ files: {
+ "/enum-constants.ts": /* ts */ `
+ export enum x {
+ a = 3,
+ b = 6,
+ }
+ `,
+ "/enum-entry.ts": /* ts */ `
+ import { x } from './enum-constants'
+ console.log([
+ +x.b,
+ -x.b,
+ ~x.b,
+ !x.b,
+ typeof x.b,
+ ], [
+ x.a + x.b,
+ x.a - x.b,
+ x.a * x.b,
+ x.a / x.b,
+ x.a % x.b,
+ x.a ** x.b,
+ ], [
+ x.a < x.b,
+ x.a > x.b,
+ x.a <= x.b,
+ x.a >= x.b,
+ x.a == x.b,
+ x.a != x.b,
+ x.a === x.b,
+ x.a !== x.b,
+ ], [
+ x.b << 1,
+ x.b >> 1,
+ x.b >>> 1,
+ ], [
+ x.a & x.b,
+ x.a | x.b,
+ x.a ^ x.b,
+ ], [
+ x.a && x.b,
+ x.a || x.b,
+ x.a ?? x.b,
+ ])
+ `,
+ "/const-constants.js": /* js */ `
+ export const a = 3
+ export const b = 6
+ `,
+ "/const-entry.js": /* js */ `
+ import { a, b } from './const-constants'
+ console.log([
+ +b,
+ -b,
+ ~b,
+ !b,
+ typeof b,
+ ], [
+ a + b,
+ a - b,
+ a * b,
+ a / b,
+ a % b,
+ a ** b,
+ ], [
+ a < b,
+ a > b,
+ a <= b,
+ a >= b,
+ a == b,
+ a != b,
+ a === b,
+ a !== b,
+ ], [
+ b << 1,
+ b >> 1,
+ b >>> 1,
+ ], [
+ a & b,
+ a | b,
+ a ^ b,
+ ], [
+ a && b,
+ a || b,
+ a ?? b,
+ ])
+ `,
+ "/nested-constants.ts": /* ts */ `
+ export const a = 2
+ export const b = 4
+ export const c = 8
+ export enum x {
+ a = 16,
+ b = 32,
+ c = 64,
+ }
+ `,
+ "/nested-entry.ts": /* ts */ `
+ import { a, b, c, x } from './nested-constants'
+ console.log({
+ 'should be 4': ~(~a & ~b) & (b | c),
+ 'should be 32': ~(~x.a & ~x.b) & (x.b | x.c),
+ })
+ `,
+ },
+ entryPoints: ["/enum-entry.ts", "/const-entry.js", "/nested-entry.ts"],
+ });
+ itBundled("dce/MultipleDeclarationTreeShaking", {
+ // GENERATED
+ files: {
+ "/var2.js": /* js */ `
+ var x = 1
+ console.log(x)
+ var x = 2
+ `,
+ "/var3.js": /* js */ `
+ var x = 1
+ console.log(x)
+ var x = 2
+ console.log(x)
+ var x = 3
+ `,
+ "/function2.js": /* js */ `
+ function x() { return 1 }
+ console.log(x())
+ function x() { return 2 }
+ `,
+ "/function3.js": /* js */ `
+ function x() { return 1 }
+ console.log(x())
+ function x() { return 2 }
+ console.log(x())
+ function x() { return 3 }
+ `,
+ },
+ entryPoints: ["/var2.js", "/var3.js", "/function2.js", "/function3.js"],
+ });
+ itBundled("dce/MultipleDeclarationTreeShakingMinifySyntax", {
+ // GENERATED
+ files: {
+ "/var2.js": /* js */ `
+ var x = 1
+ console.log(x)
+ var x = 2
+ `,
+ "/var3.js": /* js */ `
+ var x = 1
+ console.log(x)
+ var x = 2
+ console.log(x)
+ var x = 3
+ `,
+ "/function2.js": /* js */ `
+ function x() { return 1 }
+ console.log(x())
+ function x() { return 2 }
+ `,
+ "/function3.js": /* js */ `
+ function x() { return 1 }
+ console.log(x())
+ function x() { return 2 }
+ console.log(x())
+ function x() { return 3 }
+ `,
+ },
+ entryPoints: ["/var2.js", "/var3.js", "/function2.js", "/function3.js"],
+ });
+ itBundled("dce/PureCallsWithSpread", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ /* @__PURE__ */ foo(...args);
+ /* @__PURE__ */ new foo(...args);
+ `,
+ },
+ });
+ itBundled("dce/TopLevelFunctionInliningWithSpread", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ function empty1() {}
+ function empty2() {}
+ function empty3() {}
+
+ function identity1(x) { return x }
+ function identity2(x) { return x }
+ function identity3(x) { return x }
+
+ empty1()
+ empty2(args)
+ empty3(...args)
+
+ identity1()
+ identity2(args)
+ identity3(...args)
+ `,
+ "/inner.js": /* js */ `
+ export function empty1() {}
+ export function empty2() {}
+ export function empty3() {}
+
+ export function identity1(x) { return x }
+ export function identity2(x) { return x }
+ export function identity3(x) { return x }
+ `,
+ "/entry-outer.js": /* js */ `
+ import {
+ empty1,
+ empty2,
+ empty3,
+
+ identity1,
+ identity2,
+ identity3,
+ } from './inner.js'
+
+ empty1()
+ empty2(args)
+ empty3(...args)
+
+ identity1()
+ identity2(args)
+ identity3(...args)
+ `,
+ },
+ entryPoints: ["/entry.js", "/entry-outer.js"],
+ });
+ itBundled("dce/NestedFunctionInliningWithSpread", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ function empty1() {}
+ function empty2() {}
+ function empty3() {}
+
+ function identity1(x) { return x }
+ function identity2(x) { return x }
+ function identity3(x) { return x }
+
+ check(
+ empty1(),
+ empty2(args),
+ empty3(...args),
+
+ identity1(),
+ identity2(args),
+ identity3(...args),
+ )
+ `,
+ "/inner.js": /* js */ `
+ export function empty1() {}
+ export function empty2() {}
+ export function empty3() {}
+
+ export function identity1(x) { return x }
+ export function identity2(x) { return x }
+ export function identity3(x) { return x }
+ `,
+ "/entry-outer.js": /* js */ `
+ import {
+ empty1,
+ empty2,
+ empty3,
+
+ identity1,
+ identity2,
+ identity3,
+ } from './inner.js'
+
+ check(
+ empty1(),
+ empty2(args),
+ empty3(...args),
+
+ identity1(),
+ identity2(args),
+ identity3(...args),
+ )
+ `,
+ },
+ entryPoints: ["/entry.js", "/entry-outer.js"],
+ });
+ itBundled("dce/PackageJsonSideEffectsFalseCrossPlatformSlash", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import "demo-pkg/foo"
+ import "demo-pkg/bar"
+ `,
+ "/Users/user/project/node_modules/demo-pkg/foo.js": `console.log('foo')`,
+ "/Users/user/project/node_modules/demo-pkg/bar/index.js": `console.log('bar')`,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "sideEffects": [
+ "**/foo.js",
+ "bar/index.js"
+ ]
+ }
+ `,
+ },
+ });
+ 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 }`,
+ },
+ });
+});
diff --git a/test/bundler/esbuild/default.test.ts b/test/bundler/esbuild/default.test.ts
new file mode 100644
index 000000000..70630fc31
--- /dev/null
+++ b/test/bundler/esbuild/default.test.ts
@@ -0,0 +1,6136 @@
+import assert from "assert";
+import dedent from "dedent";
+import { bundlerTest, expectBundled, itBundled, testForFile } from "../expectBundled";
+var { describe, test, expect } = testForFile(import.meta.path);
+
+// Tests ported from:
+// https://github.com/evanw/esbuild/blob/main/internal/bundler_tests/bundler_default_test.go
+
+// For debug, all files are written to $TEMP/bun-bundle-tests/default
+
+describe("bundler", () => {
+ itBundled("default/SimpleES6", {
+ files: {
+ "/entry.js": /* js */ `
+ import {fn} from './foo';
+ console.log(fn());
+ `,
+ "/foo.js": /* js */ `
+ export function fn() {
+ return 123
+ }
+ `,
+ },
+ run: {
+ stdout: "123",
+ },
+ });
+ itBundled("default/SimpleCommonJS", {
+ files: {
+ "/entry.js": /* js */ `
+ const fn = require('./foo')
+ console.log(fn())
+ `,
+ "/foo.js": /* js */ `
+ module.exports = function() {
+ return 123
+ }
+ `,
+ },
+ run: {
+ stdout: "123",
+ },
+ });
+ // This test makes sure that require() calls are still recognized in nested
+ // scopes. It guards against bugs where require() calls are only recognized in
+ // the top-level module scope.
+ itBundled("default/NestedCommonJS", {
+ files: {
+ "/entry.js": /* js */ `
+ function nestedScope() {
+ const fn = require('./foo')
+ console.log(fn())
+ }
+ nestedScope()
+ `,
+ "/foo.js": /* js */ `
+ module.exports = function() {
+ return 123
+ }
+ `,
+ },
+ run: {
+ stdout: "123",
+ },
+ });
+ itBundled("default/NewExpressionCommonJS", {
+ files: {
+ "/entry.js": /* js */ `
+ new (require("./foo.js")).Foo();
+ `,
+ "/foo.js": /* js */ `
+ class Foo {}
+ module.exports = {Foo};
+ `,
+ },
+ run: true,
+ });
+ itBundled("default/CommonJSFromES6", {
+ files: {
+ "/entry.js": /* js */ `
+ const {foo} = require('./foo')
+ console.log(foo(), bar())
+ const {bar} = require('./bar') // This should not be hoisted
+ `,
+ "/foo.js": /* js */ `
+ export function foo() {
+ return 'foo'
+ }
+ `,
+ "/bar.js": /* js */ `
+ export function bar() {
+ return 'bar'
+ }
+ `,
+ },
+ run: {
+ error: "TypeError: bar2 is not a function. (In 'bar2()', 'bar2' is undefined)",
+ errorLineMatch: /console\.log/,
+ },
+ });
+ itBundled("default/ES6FromCommonJS", {
+ files: {
+ "/entry.js": /* js */ `
+ import {foo} from './foo'
+ console.log(foo(), bar())
+ import {bar} from './bar' // This should be hoisted
+ `,
+ "/foo.js": /* js */ `
+ exports.foo = function() {
+ return 'foo'
+ }
+ `,
+ "/bar.js": /* js */ `
+ exports.bar = function() {
+ return 'bar'
+ }
+ `,
+ },
+ run: {
+ stdout: "foo bar",
+ },
+ });
+ itBundled("default/NestedES6FromCommonJS", {
+ files: {
+ "/entry.js": /* js */ `
+ import {fn} from './foo'
+ (() => {
+ console.log(fn())
+ })()
+ `,
+ "/foo.js": /* js */ `
+ exports.fn = function() {
+ return 123
+ }
+ `,
+ },
+ run: {
+ stdout: "123",
+ },
+ });
+ itBundled("default/ExportFormsES6", {
+ files: {
+ "/entry.js": /* js */ `
+ export default 123
+ export var v = 234
+ export let l = 345
+ export const c = 456
+ export {Class as C}
+ export function Fn() {}
+ export class Class {}
+ export * from './a'
+ export * as b from './b'
+ `,
+ "/a.js": "export const abc = undefined",
+ "/b.js": "export const xyz = null",
+
+ "/test.js": /* js */ `
+ import * as module from "./out";
+ import { strictEqual } from "node:assert";
+
+ strictEqual(module.default, 123, ".default");
+ strictEqual(module.v, 234, ".v");
+ strictEqual(module.l, 345, ".l");
+ strictEqual(module.c, 456, ".c");
+ module.Fn();
+ new module.C();
+ strictEqual('abc' in module, true, ".abc exists");
+ strictEqual(module.abc, undefined, ".abc");
+ strictEqual(module.b.xyz, null, ".xyz");
+ `,
+ },
+ format: "esm",
+ run: {
+ file: "/test.js",
+ },
+ });
+ itBundled("default/ExportFormsIIFE", {
+ files: {
+ "/entry.js": /* js */ `
+ export default 123
+ export var v = 234
+ export let l = 345
+ export const c = 456
+ export {Class as C}
+ export function Fn() {}
+ export class Class {}
+ export * from './a'
+ export * as b from './b'
+ `,
+ "/a.js": "export const abc = undefined",
+ "/b.js": "export const xyz = null",
+ },
+ format: "iife",
+ globalName: "globalName",
+ run: true,
+ onAfterBundle(api) {
+ api.appendFile(
+ "/out.js",
+ dedent/* js */ `
+ import { strictEqual } from "node:assert";
+ strictEqual(globalName.default, 123, ".default");
+ strictEqual(globalName.v, 234, ".v");
+ strictEqual(globalName.l, 345, ".l");
+ strictEqual(globalName.c, 456, ".c");
+ globalName.Fn();
+ new globalName.C();
+ strictEqual("abc" in globalName, true, ".abc exists");
+ strictEqual(globalName.abc, undefined, ".abc");
+ strictEqual(globalName.b.xyz, null, ".xyz");
+ `,
+ );
+ },
+ });
+ itBundled("default/ExportFormsWithMinifyIdentifiersAndNoBundle", {
+ files: {
+ "/a.js": /* js */ `
+ export default 123
+ export var varName = 234
+ export let letName = 345
+ export const constName = 456
+ function Func2() {}
+ class Class2 {}
+ export {Class as Cls, Func2 as Fn2, Class2 as Cls2}
+ export function Func() {}
+ export class Class {}
+ export * from './f'
+ export * as fromF from './f'
+ `,
+ "/b.js": "export default function() {}",
+ "/c.js": "export default function foo() {}",
+ "/d.js": "export default class {}",
+ "/e.js": "export default class Foo {}",
+ },
+ entryPoints: ["/a.js", "/b.js", "/c.js", "/d.js", "/e.js"],
+ mode: "transform",
+ runtimeFiles: {
+ "./out/f.js": /* js */ `
+ export const f = 987;
+ `,
+ "/test.js": /* js */ `
+ import * as a from './out/a';
+ import { deepEqual } from 'node:assert';
+ deepEqual(a.varName, 234, "a.default");
+ deepEqual(a.letName, 345, "a.letName");
+ deepEqual(a.constName, 456, "a.constName");
+ a.Fn2();
+ new a.Cls();
+ new a.Cls2();
+ new a.Class();
+ deepEqual(a.f, 987, "a.f");
+ deepEqual(a.fromF, { f: 987 }, "a.fromF");
+ `,
+ },
+ run: {
+ file: "/test.js",
+ },
+ });
+ // this two were edited heavily. They used to be all importing from `foo`, but here i have it
+ // so the modules can actually be resolved at runtime.
+ const importFormsConfig = {
+ files: {
+ "/entry.js": /* js */ `
+ import './a'
+ import {} from './b'
+ import * as ns from './c'
+ import {a, b as c} from './c'
+ import def from './c'
+ import def2, * as ns2 from './c'
+ import def3, {a2, b as c3} from './c'
+ const imp = [
+ await import('./c'),
+ function nested() { return import('./c') },
+ ]
+
+ import { deepEqual } from 'node:assert'
+ deepEqual(a, 1, 'a');
+ deepEqual(a2, 4, 'a2');
+ deepEqual(c3, 2, 'c3');
+ deepEqual(def, 3, 'def');
+ deepEqual(def2, 3, 'def2');
+ deepEqual(def3, 3, 'def3');
+ deepEqual(ns, ns2, 'ns and ns2');
+ deepEqual(ns, imp[0], 'ns and first await import');
+ deepEqual(ns, await imp[1](), 'ns and second import');
+ `,
+ },
+ runtimeFiles: {
+ "/a.js": /* js */ `
+ globalThis.aWasImported = true;
+ `,
+ "/b.js": /* js */ `
+ globalThis.bWasImported = true;
+ `,
+ "/c.js": /* js */ `
+ export const a = 1;
+ export const b = 2;
+ export default 3;
+ export const a2 = 4;
+ `,
+ "/test.js": String.raw/* js */ `
+ import './out.js';
+ if (!globalThis.aWasImported) {
+ throw new Error('"import \'./a\'" was tree-shaken when it should not have been.')
+ }
+ if (!globalThis.bWasImported) {
+ throw new Error('"import {} from \'./b\'" was tree-shaken when it should not have been.')
+ }
+ `,
+ },
+ mode: "transform",
+ run: {
+ file: "/test.js",
+ },
+ } as const;
+ itBundled("default/ImportFormsWithNoBundle", {
+ ...importFormsConfig,
+ });
+ itBundled("default/ImportFormsWithMinifyIdentifiersAndNoBundle", {
+ ...importFormsConfig,
+ minifyIdentifiers: true,
+ });
+ itBundled("default/ExportFormsCommonJS", {
+ files: {
+ "/entry.js": /* js */ `
+ const commonjs = require("./commonjs");
+ const c = require("./c").default;
+ const d = require("./d").default;
+ const e = require("./e").default;
+ const f = require("./f").default;
+ const g = require("./g").default;
+ const h = require("./h").default;
+
+ assert.deepEqual(commonjs.default, 123, "commonjs.default");
+ assert.deepEqual(commonjs.v, 234, "commonjs.default");
+ assert.deepEqual(commonjs.l, 345, "commonjs.l");
+ assert.deepEqual(commonjs.c, 456, "commonjs.c");
+ commonjs.Fn();
+ new commonjs.C();
+ new commonjs.Class();
+ new commonjs.C();
+ assert("abc" in commonjs, "commonjs.abc");
+ assert.deepEqual(commonjs.abc, undefined, "commonjs.abc");
+ assert.deepEqual(commonjs.b, { xyz: null }, "commonjs.b");
+ new c();
+ new d();
+ assert.deepEqual(d.prop, 567, "d.prop");
+ e();
+ f();
+ assert.deepEqual(f.prop, 678, "f.prop");
+ assert(g() instanceof Promise, "g");
+ assert(h() instanceof Promise, "h");
+ assert.deepEqual(h.prop, 789, "h.prop");
+ `,
+ "/commonjs.js": /* js */ `
+ export default 123
+ export var v = 234
+ export let l = 345
+ export const c = 456
+ export {Class as C}
+ export function Fn() {}
+ export class Class {}
+ export * from './a'
+ export * as b from './b'
+ `,
+ "/a.js": `export const abc = undefined`,
+ "/b.js": `export const xyz = null`,
+ "/c.js": `export default class {}`,
+ "/d.js": `export default class Foo {} Foo.prop = 567`,
+ "/e.js": `export default function() {}`,
+ "/f.js": `export default function foo() {} foo.prop = 678`,
+ "/g.js": `export default async function() {}`,
+ "/h.js": `export default async function foo() {} foo.prop = 789`,
+
+ // assert bundles weird as of writing
+ "/test.js": /* js */ `
+ globalThis.assert = import.meta.require('assert');
+ require('./out.js');
+ `,
+ },
+ run: {
+ file: "/test.js",
+ },
+ });
+ itBundled("default/ExportChain", {
+ files: {
+ "/entry.js": `export {b as a} from './foo'`,
+ "/foo.js": `export {c as b} from './bar'`,
+ "/bar.js": `export const c = 123`,
+
+ "/test.js": `
+ import { strictEqual } from 'assert';
+ import * as module from './out';
+ strictEqual(module.a, 123);
+ `,
+ },
+ run: {
+ file: "/test.js",
+ },
+ });
+ itBundled("default/ExportInfiniteCycle1", {
+ files: {
+ "/entry.js": /* js */ `
+ export {a as b} from './entry'
+ export {b as c} from './entry'
+ export {c as d} from './entry'
+ export {d as a} from './entry'
+ `,
+ },
+ bundleErrors: {
+ "/entry.js": [
+ `Detected cycle while resolving import "a"`,
+ `Detected cycle while resolving import "b"`,
+ `Detected cycle while resolving import "c"`,
+ `Detected cycle while resolving import "d"`,
+ ],
+ },
+ });
+ itBundled("default/ExportInfiniteCycle2", {
+ files: {
+ "/entry.js": /* js */ `
+ export {a as b} from './foo'
+ export {c as d} from './foo'
+ `,
+ "/foo.js": /* js */ `
+ export {b as c} from './entry'
+ export {d as a} from './entry'
+ `,
+ },
+ bundleErrors: {
+ "/entry.js": [`Detected cycle while resolving import "a"`, `Detected cycle while resolving import "c"`],
+ "/foo.js": [`Detected cycle while resolving import "b"`, `Detected cycle while resolving import "d"`],
+ },
+ });
+ itBundled("default/JSXImportsCommonJS", {
+ files: {
+ "/entry.jsx": /* jsx */ `
+ import {elem, frag} from './custom-react'
+ console.log(<div/>, <>fragment</>)
+ `,
+ "/custom-react.js": /* js */ `
+ module.exports = {
+ elem: (...args) => console.log('elem', ...args),
+ frag: 'frag',
+ };
+ `,
+ },
+ jsx: {
+ factory: "elem",
+ fragment: "frag",
+ runtime: "automatic",
+ },
+ run: {
+ stdout: `
+ elem div null
+ elem frag null fragment
+ undefined undefined
+ `,
+ },
+ });
+ itBundled("default/JSXImportsES6", {
+ files: {
+ "/entry.jsx": /* jsx */ `
+ import {elem, frag} from './custom-react'
+ console.log(<div/>, <>fragment</>)
+ `,
+ "/custom-react.js": /* js */ `
+ export function elem(...args) {
+ console.log('elem', ...args)
+ }
+ export const frag = "frag";
+ `,
+ },
+ jsx: {
+ factory: "elem",
+ fragment: "frag",
+ },
+ run: {
+ stdout: `
+ elem div null
+ elem frag null fragment
+ undefined undefined
+ `,
+ },
+ });
+ itBundled("default/JSXSyntaxInJS", {
+ files: {
+ "/entry.js": `console.log(<div/>)`,
+ },
+ bundleErrors: {
+ "/entry.js": ["ERROR: The JSX syntax extension is not currently enabled"],
+ },
+ });
+ itBundled("default/JSXConstantFragments", {
+ files: {
+ "/entry.js": /* js */ `
+ import './default'
+ import './null'
+ import './boolean'
+ import './number'
+ import './string-single-empty'
+ import './string-double-empty'
+ import './string-single-punctuation'
+ import './string-double-punctuation'
+ import './string-template'
+ `,
+ "/default.jsx": `console.log(<></>)`,
+ "/null.jsx": `console.log(<></>) // @jsxFrag null`,
+ "/boolean.jsx": `console.log(<></>) // @jsxFrag true`,
+ "/number.jsx": `console.log(<></>) // @jsxFrag 123`,
+ "/string-single-empty.jsx": `console.log(<></>) // @jsxFrag ''`,
+ "/string-double-empty.jsx": `console.log(<></>) // @jsxFrag ""`,
+ "/string-single-punctuation.jsx": `console.log(<></>) // @jsxFrag '['`,
+ "/string-double-punctuation.jsx": `console.log(<></>) // @jsxFrag "["`,
+ "/string-template.jsx": "console.log(<></>) // @jsxFrag ``",
+
+ "/test.js": /* js */ `
+ globalThis.React = {
+ createElement: (x) => x,
+ Fragment: 'frag'
+ }
+ await import('./out.js');
+ `,
+ },
+ jsx: {
+ fragment: "']'",
+ },
+ bundleWarnings: {
+ "/string-template.jsx": ["Invalid JSX fragment: ``"],
+ },
+ run: {
+ file: "/test.js",
+ stdout: "]\nnull\ntrue\n123\n\n\n[\n[\n]",
+ },
+ });
+ itBundled("default/JSXAutomaticImportsCommonJS", {
+ files: {
+ "/entry.jsx": /* jsx */ `
+ import {jsx, Fragment} from './custom-react'
+ console.log(<div jsx={jsx}/>, <><Fragment/></>)
+ `,
+ "/custom-react.js": `module.exports = { jsx: 'jsx', Fragment: 'fragment2' }`,
+ },
+ jsx: {
+ automaticRuntime: true,
+ },
+ external: ["react"],
+ run: {
+ stdout: `
+ <div jsx="jsx" /> <>
+ <fragment2 />
+ </>
+ `,
+ },
+ });
+ itBundled("default/JSXAutomaticImportsES6", {
+ files: {
+ "/entry.jsx": /* jsx */ `
+ import {jsx, Fragment} from './custom-react'
+ console.log(<div jsx={jsx}/>, <><Fragment/></>)
+ `,
+ "/custom-react.js": /* js */ `
+ export const jsx = 'jsx function'
+ export const Fragment = 'fragment'
+ `,
+ },
+ jsx: {
+ automaticRuntime: true,
+ },
+ external: ["react"],
+ run: {
+ stdout: `
+ <div jsx="jsx function" /> <>
+ <fragment />
+ </>
+ `,
+ },
+ });
+ itBundled("default/JSXAutomaticSyntaxInJS", {
+ files: {
+ "/entry.mjs": `console.log(<div/>)`,
+ },
+ jsx: {
+ automaticRuntime: true,
+ },
+ external: ["react"],
+ bundleErrors: {
+ "/entry.mjs": ["The JSX syntax extension is not currently enabled"],
+ },
+ });
+ itBundled("default/NodeModules", {
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import fn from 'demo-pkg'
+ console.log(fn())
+ `,
+ "/Users/user/project/node_modules/demo-pkg/index.js": /* js */ `
+ module.exports = function() {
+ return 123
+ }
+ `,
+ },
+ run: {
+ stdout: "123",
+ },
+ });
+ itBundled("default/RequireChildDirCommonJS", {
+ files: {
+ "/Users/user/project/src/entry.js": `console.log(require('./dir'))`,
+ "/Users/user/project/src/dir/index.js": `module.exports = 123`,
+ },
+ run: {
+ stdout: "123",
+ },
+ });
+ itBundled("default/RequireChildDirES6", {
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import value from './dir'
+ console.log(value)
+ `,
+ "/Users/user/project/src/dir/index.js": `export default 123`,
+ },
+ run: {
+ stdout: "123",
+ },
+ });
+ itBundled("default/RequireParentDirCommonJS", {
+ files: {
+ "/Users/user/project/src/dir/entry.js": `console.log(require('..'))`,
+ "/Users/user/project/src/index.js": `module.exports = 123`,
+ },
+ run: {
+ stdout: "123",
+ },
+ });
+ itBundled("default/RequireParentDirES6", {
+ files: {
+ "/Users/user/project/src/dir/entry.js": /* js */ `
+ import value from '..'
+ console.log(value)
+ `,
+ "/Users/user/project/src/index.js": `export default 123`,
+ },
+ run: {
+ stdout: "123",
+ },
+ });
+ itBundled("default/ImportMissingES6", {
+ files: {
+ "/entry.js": /* js */ `
+ import fn, {x as a, y as b} from './foo'
+ console.log(fn(a, b))
+ `,
+ "/foo.js": `export const x = 123`,
+ },
+ bundleErrors: {
+ "/entry.js": [
+ `No matching export "default" in "foo.js" for import "default"`,
+ `No matching export "y" in "foo.js" for import "y"`,
+ ],
+ },
+ });
+ itBundled("default/ImportMissingUnusedES6", {
+ files: {
+ "/entry.js": `import fn, {x as a, y as b} from './foo'`,
+ "/foo.js": `export const x = 123`,
+ },
+ bundleErrors: {
+ "/entry.js": [
+ `No matching export "default" in "foo.js" for import "default"`,
+ `No matching export "y" in "foo.js" for import "y"`,
+ ],
+ },
+ });
+ itBundled("default/ImportMissingCommonJS", {
+ files: {
+ "/entry.js": /* js */ `
+ import fn, {x as a, y as b} from './foo'
+ console.log(fn.x, a, b);
+ `,
+ "/foo.js": `exports.x = 123`,
+ },
+ run: {
+ stdout: "123 123 undefined",
+ },
+ });
+ itBundled("default/ImportMissingNeitherES6NorCommonJS", {
+ files: {
+ "/named.js": /* js */ `
+ import fn, {x as a, y as b} from './foo'
+ console.log(fn(a, b))
+ `,
+ "/star.js": /* js */ `
+ import * as ns from './foo'
+ console.log(ns.default(ns.x, ns.y))
+ `,
+ "/star-capture.js": /* js */ `
+ import * as ns from './foo'
+ console.log(ns)
+ `,
+ "/bare.js": `import './foo'`,
+ "/require.js": `console.log(require('./foo'))`,
+ "/import.js": `console.log(import('./foo'))`,
+ "/foo.js": `console.log('no exports here')`,
+ },
+ entryPoints: ["/named.js", "/star.js", "/star-capture.js", "/bare.js", "/require.js", "/import.js"],
+ // TODO: warnings
+ bundleWarnings: {
+ "/named.js": [
+ 'Import "x" will always be undefined because the file "foo.js" has no exports',
+ 'Import "y" will always be undefined because the file "foo.js" has no exports',
+ ],
+ "/star.js": [
+ 'Import "x" will always be undefined because the file "foo.js" has no exports',
+ 'Import "y" will always be undefined because the file "foo.js" has no exports',
+ ],
+ },
+ });
+ itBundled("default/ExportMissingES6", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './foo'
+ console.log(ns)
+ `,
+ "/foo.js": `export {nope} from './bar'`,
+ "/bar.js": `export const yep = 123`,
+ },
+ bundleErrors: {
+ "/foo.js": [`No matching export "nope" in "bar.js" for import "nope"`],
+ },
+ });
+ itBundled("default/DotImport", {
+ files: {
+ "/entry.js": /* js */ `
+ import {x} from '.'
+ console.log(x)
+ `,
+ "/index.js": `exports.x = 123`,
+ },
+ run: {
+ stdout: "123",
+ },
+ });
+ itBundled("default/RequireWithTemplate", {
+ files: {
+ "/a.js": `
+ console.log(require('./b').x)
+ console.log(require(\`./b\`).x)
+ `,
+ "/b.js": `exports.x = 123`,
+ },
+ run: {
+ stdout: "123\n123",
+ },
+ });
+ itBundled("default/DynamicImportWithTemplateIIFE", {
+ files: {
+ "/a.js": `
+ import('./b').then(ns => console.log(ns.x))
+ import(\`./b\`).then(ns => console.log(ns.x))
+ `,
+ "/b.js": `exports.x = 123`,
+ },
+ format: "iife",
+ run: {
+ stdout: "123\n123",
+ },
+ });
+ itBundled("default/RequireAndDynamicImportInvalidTemplate", {
+ files: {
+ "/entry.js": `
+ require(tag\`./b\`)
+ require(\`./\${b}\`)
+
+ try {
+ require(tag\`./b\`)
+ require(\`./\${b}\`)
+ } catch {
+ }
+
+ (async () => {
+ import(tag\`./b\`)
+ import(\`./\${b}\`)
+ await import(tag\`./b\`)
+ await import(\`./\${b}\`)
+
+ try {
+ import(tag\`./b\`)
+ import(\`./\${b}\`)
+ await import(tag\`./b\`)
+ await import(\`./\${b}\`)
+ } catch {
+ }
+ })()
+ `,
+
+ "/test.js": `
+ globalThis.tag = () => './c.js';
+ globalThis.b = 'c.js';
+ import('./out');
+ `,
+ "/c.js": `console.log("c")`,
+ },
+ run: {
+ file: "/test.js",
+ stdout: "c",
+ },
+ });
+ itBundled("default/DynamicImportWithExpressionCJS", {
+ files: {
+ "/a.js": /* js */ `
+ import('foo')
+ import(foo())
+ `,
+ },
+ format: "cjs",
+ mode: "transform",
+ onAfterBundle(api) {
+ api.expectFile("/out.js").toContain('import("foo")');
+ api.expectFile("/out.js").toContain("import(foo())");
+ },
+ });
+ itBundled("default/MinifiedDynamicImportWithExpressionCJS", {
+ files: {
+ "/a.js": /* js */ `
+ import('foo')
+ import(foo())
+ `,
+ },
+ format: "cjs",
+ mode: "transform",
+ minifyWhitespace: true,
+ onAfterBundle(api) {
+ api.expectFile("/out.js").toContain('import("foo")');
+ api.expectFile("/out.js").toContain("import(foo())");
+ },
+ });
+ itBundled("default/ConditionalRequireResolve", {
+ files: {
+ "/a.js": /* js */ `
+ require.resolve(x ? 'a' : y ? 'b' : 'c')
+ require.resolve(v ? y ? 'a' : 'b' : c)
+ `,
+ },
+ platform: "node",
+ format: "cjs",
+ // esbuild seems to not need externals for require.resolve, but it should be specified
+ external: ["a", "b", "c"],
+ onAfterBundle(api) {
+ api.expectFile("/out.js").toContain('x ? require.resolve("a") : y ? require.resolve("b") : require.resolve("c")');
+ api.expectFile("/out.js").toContain('v ? y ? require.resolve("a") : require.resolve("b") : require.resolve(c)');
+ },
+ });
+ itBundled("default/ConditionalRequire", {
+ files: {
+ "/a.js": /* js */ `
+ const x = process.argv[2] === 'true';
+ const y = process.argv[3] === 'true';
+ const c = process.argv[4];
+
+ console.log(require(x ? 'a' : y ? './b' : 'c').foo)
+ console.log(require(x ? y ? 'a' : './b' : c).foo)
+ `,
+ "/b.js": `exports.foo = 213`,
+ },
+ external: ["a", "c"],
+ runtimeFiles: {
+ "/b.js": `throw new Error("Did not bundle b.js")`,
+ "/c.js": `exports.foo = 532`,
+ "/node_modules/a/index.js": `exports.foo = 852`,
+ "/node_modules/a/package.json": `{"main": "index.js", "name": "a"}`,
+ "/node_modules/c/index.js": `exports.foo = 123`,
+ "/node_modules/c/package.json": `{"main": "index.js", "name": "c"}`,
+ },
+ run: [
+ {
+ args: ["true", "true", "./c.js"],
+ stdout: "852\n852",
+ },
+ {
+ args: ["true", "false", "./c.js"],
+ stdout: "852\n213",
+ },
+ {
+ args: ["false", "true", "./c.js"],
+ stdout: "213\n532",
+ },
+ {
+ args: ["false", "false", "./c.js"],
+ stdout: "123\n532",
+ },
+ ],
+ });
+ itBundled("default/ConditionalImport", {
+ files: {
+ "/a.js": `console.log('a', (await import(x ? 'a' : y ? './import' : 'c')).foo)`,
+ "/b.js": `console.log('b', (await import(x ? y ? 'a' : './import' : c)).foo)`,
+ "/import.js": `exports.foo = 213`,
+ },
+ runtimeFiles: {
+ "/node_modules/a/index.js": "export const foo = 'a'",
+ "/node_modules/b/index.js": "export const foo = 'b'",
+ "/node_modules/c/index.js": "export const foo = 'c'",
+ "/node_modules/d/index.js": "export const foo = 'd'",
+
+ "/test.js": /* js */ `
+ globalThis.x = process.argv[2] === 'true';
+ globalThis.y = process.argv[3] === 'true';
+ globalThis.c = process.argv[4];
+ await import('./out/a');
+ await import('./out/b');
+ `,
+ },
+ entryNames: "[name].[ext]",
+ entryPoints: ["/a.js", "/b.js"],
+ external: ["a", "b", "c"],
+ run: [
+ {
+ file: "/test.js",
+ args: ["true", "true", "d"],
+ stdout: "a a\nb a",
+ },
+ {
+ file: "/test.js",
+ args: ["true", "false", "d"],
+ stdout: "a a\nb 213",
+ },
+ {
+ file: "/test.js",
+ args: ["false", "true", "d"],
+ stdout: "a 213\nb d",
+ },
+ {
+ file: "/test.js",
+ args: ["false", "false", "d"],
+ stdout: "a c\nb d",
+ },
+ ],
+ });
+ itBundled("default/RequireBadArgumentCount", {
+ files: {
+ "/entry.js": /* js */ `
+ require()
+ require("a", "b")
+
+ try {
+ require()
+ require("a", "b")
+ } catch {
+ }
+ `,
+ },
+ onAfterBundle(api) {
+ api.prependFile(
+ "/out.js",
+ /* js */ `
+ const require = (...args) => console.log('require:' + args.join(','));
+ `,
+ );
+ },
+ run: {
+ stdout: `
+ require:
+ require:a,b
+ require:
+ require:a,b
+ `,
+ },
+ });
+ itBundled("default/RequireJson", {
+ files: {
+ "/entry.js": `console.log(JSON.stringify(require('./test.json')))`,
+ "/test.json": /* json */ `
+ {
+ "a": true,
+ "b": 123,
+ "c": [null]
+ }
+ `,
+ },
+ run: {
+ stdout: '{"a":true,"b":123,"c":[null]}',
+ },
+ });
+ itBundled("default/RequireTxt", {
+ files: {
+ "/entry.js": `console.log(require('./test.txt'))`,
+ "/test.txt": `This is a test.`,
+ },
+ run: {
+ stdout: "This is a test.",
+ },
+ });
+ itBundled("default/RequireBadExtension", {
+ files: {
+ "/entry.js": `console.log(require('./test.bad'))`,
+ "/test.bad": `This is a test.`,
+ },
+ bundleErrors: {
+ "/entry.js": ['No loader is configured for ".bad" files: test.bad'],
+ },
+ });
+ itBundled("default/FalseRequire", {
+ files: {
+ "/entry.js": `(require => require('./test.txt'))(console.log)`,
+ "/test.txt": `Failed.`,
+ },
+ run: {
+ stdout: "./test.txt",
+ },
+ });
+ itBundled("default/RequireWithoutCall", {
+ // TODO: MANUAL CHECK: `require` on line one has to be renamed to `__require`
+ files: {
+ "/entry.js": /* js */ `
+ const req = require
+ req('./entry')
+ `,
+ },
+ platform: "neutral",
+ });
+ itBundled("default/NestedRequireWithoutCall", {
+ // TODO: MANUAL CHECK: `require` on line one has to be renamed to `__require`
+ files: {
+ "/entry.js": /* js */ `
+ (() => {
+ const req = require
+ req('./entry')
+ })()
+ `,
+ },
+ platform: "neutral",
+ });
+ itBundled("default/RequireWithCallInsideTry", {
+ files: {
+ "/entry.js": /* js */ `
+ try {
+ const supportsColor = require('not-supports-color'); // bun overrides supports-color
+ if (supportsColor && (supportsColor.stderr || supportsColor).level >= 2) {
+ exports.colors = [];
+ }
+ } catch (error) {
+ }
+ `,
+ },
+ runtimeFiles: {
+ "/test1.js": /* js */ `
+ globalThis.requireThrows = false;
+ import assert from 'assert';
+ assert.deepEqual((await import('./out')).default, { colors: [] })
+ `,
+ "/test2.js": /* js */ `
+ globalThis.requireThrows = true;
+ import assert from 'assert';
+ assert.deepEqual((await import('./out')).default, { })
+ `,
+ "/node_modules/not-supports-color/index.js": /* js */ `
+ if (requireThrows) {
+ throw new Error('This should have been caught!');
+ }
+ module.exports = { stderr: { level: 9001 } }
+ `,
+ },
+ run: [{ file: "/test1.js" }, { file: "/test2.js" }],
+ });
+ itBundled("default/RequireWithoutCallInsideTry", {
+ // TODO: MANUAL CHECK: `require` on line one has to be renamed to `__require`
+ files: {
+ "/entry.js": /* js */ `
+ try {
+ oldLocale = globalLocale._abbr;
+ var aliasedRequire = require;
+ aliasedRequire('./locale/' + name);
+ getSetGlobalLocale(oldLocale);
+ } catch (e) {}
+ `,
+ },
+ platform: "neutral",
+ });
+ itBundled("default/RequirePropertyAccessCommonJS", {
+ files: {
+ "/entry.js": /* js */ `
+ // These shouldn't warn since the format is CommonJS
+ console.log(Object.keys(require.cache))
+ console.log(Object.keys(require.extensions))
+ delete require.cache['fs']
+ delete require.extensions['.json']
+ `,
+ },
+ platform: "node",
+ format: "cjs",
+ onAfterBundle(api) {
+ api.prependFile(
+ "/out.js",
+ /* js */ `
+ const require = { cache: { fs: 'hello' }, extensions: { '.json': 'json' } };
+ `,
+ );
+ },
+ run: {
+ stdout: '[ "fs" ]\n[ ".json" ]',
+ },
+ });
+ itBundled("default/AwaitImportInsideTry", {
+ files: {
+ "/entry.js": /* js */ `
+ async function main(name) {
+ try {
+ return await import(name)
+ } catch {
+ }
+ }
+ main('fs')
+ `,
+ },
+ run: true,
+ });
+ itBundled("default/ImportInsideTry", {
+ files: {
+ "/entry.js": /* js */ `
+ let x
+ try {
+ x = import('nope1')
+ x = await import('nope2')
+ } catch {
+ }
+ `,
+ },
+ bundleErrors: {
+ "/entry.js": ['Could not resolve "nope1"'],
+ },
+ });
+ itBundled("default/ImportThenCatch", {
+ files: {
+ "/entry.js": /* js */ `
+ import(name).then(pass, fail)
+ import(name).then(pass).catch(fail)
+ import(name).catch(fail)
+ `,
+ },
+ onAfterBundle(api) {
+ // Define pass, fail, and replace `import` with a mock function. This allows for a single run
+ // and no reliance on any `import` calls, since bundler should have left it alone anyways.
+ const content = api.readFile("/out.js");
+ api.writeFile(
+ "/out.js",
+ dedent`
+ const pass = 'pass';
+ const fail = 'fail';
+ const _fn = (name) => (...args) => {
+ console.log(name, ...args);
+ return { then: _fn('then'), "catch": _fn('catch') };
+ };
+ const _import = _fn('import');
+ ` + content.replace(/import\(name\)/g, "_import()"),
+ );
+ },
+ run: {
+ stdout: "import\nthen pass fail\nimport\nthen pass\ncatch fail\nimport\ncatch fail",
+ },
+ });
+ itBundled("default/SourceMap", {
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import {bar} from './bar'
+ function foo() { bar() }
+ foo()
+ `,
+ "/Users/user/project/src/bar.js": `export function bar() { console.log('hi') }`,
+ },
+ outfile: "/Users/user/project/out.js",
+ sourceMap: true,
+ onAfterBundle(api) {
+ api.assertFileExists("/Users/user/project/out.js.map");
+ api.expectFile("/Users/user/project/out.js").toContain("//# sourceMappingURL=out.js.map");
+ },
+ run: {
+ stdout: "hi",
+ },
+ });
+ // This test covers a bug where a "var" in a nested scope did not correctly
+ // bind with references to that symbol in sibling scopes. Instead, the
+ // references were incorrectly considered to be unbound even though the symbol
+ // should be hoisted. This caused the renamer to name them different things to
+ // avoid a collision, which changed the meaning of the code.
+ itBundled("default/NestedScopeBug", {
+ files: {
+ "/entry.js": /* js */ `
+ (() => {
+ function a() {
+ b()
+ }
+ {
+ var b = () => {}
+ }
+ a()
+ })()
+ `,
+ },
+ run: true,
+ });
+ itBundled("default/HashbangBundle", {
+ files: {
+ "/entry.js": /* js */ `
+ #!/usr/bin/env a
+ import {code} from './code'
+ process.exit(code)
+ `,
+ "/code.js": /* js */ `
+ #!/usr/bin/env b
+ export const code = 0
+ `,
+ },
+ onAfterBundle(api) {
+ assert(api.readFile("/out.js").startsWith("#!/usr/bin/env a"), "hashbang exists on bundle");
+ },
+ });
+ itBundled("default/HashbangNoBundle", {
+ files: {
+ "/entry.js": /* js */ `
+ #!/usr/bin/env node
+ process.exit(0);
+ `,
+ },
+ mode: "transform",
+ onAfterBundle(api) {
+ assert(api.readFile("/out.js").startsWith("#!/usr/bin/env node"), "hashbang exists on bundle");
+ },
+ });
+ itBundled("default/HashbangBannerUseStrictOrder", {
+ files: {
+ "/entry.js": /* js */ `
+ #! in file
+ 'use strict'
+ foo()
+ `,
+ },
+ banner: "#! from banner",
+ onAfterBundle(api) {
+ assert(api.readFile("/out.js").startsWith("#! in file"), "hashbang from banner does not override file hashbang");
+ },
+ });
+ itBundled("default/RequireFSBrowser", {
+ files: {
+ "/entry.js": `console.log(require('fs'))`,
+ },
+ platform: "browser",
+ bundleErrors: {
+ "/entry.js": ['ERROR: Could not resolve "fs"'],
+ },
+ });
+ itBundled("default/RequireFSNode", {
+ files: {
+ "/entry.js": `console.log('existsSync' in require('fs'))`,
+ },
+ format: "cjs",
+ platform: "node",
+ run: {
+ stdout: "true",
+ },
+ });
+ itBundled("default/RequireFSNodeMinify", {
+ files: {
+ "/entry.js": `console.log('existsSync' in require('fs'))`,
+ },
+ minifyWhitespace: true,
+ format: "cjs",
+ platform: "node",
+ run: {
+ stdout: "true",
+ },
+ });
+ itBundled("default/ImportFSBrowser", {
+ files: {
+ "/entry.js": /* js */ `
+ import 'fs'
+ import * as fs from 'fs'
+ import defaultValue from 'fs'
+ import {readFileSync} from 'fs'
+ console.log(fs, readFileSync, defaultValue)
+ `,
+ },
+ bundleErrors: {
+ "/entry.js": ['ERROR: Could not resolve "fs"'],
+ },
+ platform: "browser",
+ });
+ itBundled("default/ImportFSNodeCommonJS", {
+ files: {
+ "/entry.js": /* js */ `
+ import 'fs'
+ import * as fs from 'fs'
+ import defaultValue from 'fs'
+ import {readFileSync} from 'fs'
+ console.log('writeFileSync' in fs, readFileSync, 'writeFileSync' in defaultValue)
+ `,
+ },
+ platform: "node",
+ format: "cjs",
+ run: {
+ stdout: "true [Function: readFileSync] true",
+ },
+ });
+ itBundled("default/ImportFSNodeES6", {
+ files: {
+ "/entry.js": /* js */ `
+ import 'fs'
+ import * as fs from 'fs'
+ import defaultValue from 'fs'
+ import {readFileSync} from 'fs'
+ console.log('writeFileSync' in fs, readFileSync, 'writeFileSync' in defaultValue)
+ `,
+ },
+ platform: "node",
+ run: {
+ stdout: "true [Function: readFileSync] true",
+ },
+ });
+ itBundled("default/ExportFSBrowser", {
+ files: {
+ "/entry.js": /* js */ `
+ export * as fs from 'fs'
+ export {readFileSync} from 'fs'
+ `,
+ },
+ platform: "browser",
+ bundleErrors: {
+ "/entry.js": ['ERROR: Could not resolve "fs"'],
+ },
+ });
+ itBundled("default/ExportFSNode", {
+ files: {
+ "/entry.js": /* js */ `
+ export * as fs from 'fs'
+ export {readFileSync} from 'fs'
+ `,
+
+ "/test.js": /* js */ `
+ import fs from "fs";
+ import assert from "assert";
+ import * as module from './out.js';
+ assert(module.fs === fs, 'export * as fs from "fs"; works')
+ assert(module.readFileSync === fs.readFileSync, 'export {readFileSync} from "fs"; works')
+ `,
+ },
+ platform: "node",
+ run: {
+ file: "/test.js",
+ },
+ });
+ itBundled("default/ReExportFSNode", {
+ files: {
+ "/entry.js": /* js */ `
+ export {fs as f} from './foo'
+ export {readFileSync as rfs} from './foo'
+ `,
+ "/foo.js": /* js */ `
+ export * as fs from 'fs'
+ export {readFileSync} from 'fs'
+ `,
+
+ "/test.js": /* js */ `
+ import fs from "fs";
+ import assert from "assert";
+ import * as module from './out.js';
+ assert(module.f === fs, 'export {fs as f} works')
+ assert(module.rfs === fs.readFileSync, 'export {rfs} works')
+ `,
+ },
+ platform: "node",
+ run: {
+ file: "/test.js",
+ },
+ });
+ itBundled("default/ExportFSNodeInCommonJSModule", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as fs from 'fs'
+ import {readFileSync} from 'fs'
+ exports.fs = fs
+ exports.readFileSync = readFileSync
+ exports.foo = 123
+ `,
+
+ "/test.js": /* js */ `
+ import fs from "fs";
+ import assert from "assert";
+ import module from './out.js';
+ assert(module.fs === fs, 'exports.fs')
+ assert(module.readFileSync === fs.readFileSync, 'exports.readFileSync')
+ assert(module.foo === 123, 'exports.foo')
+ `,
+ },
+ platform: "node",
+ run: {
+ file: "/test.js",
+ },
+ });
+ itBundled("default/ExportWildcardFSNodeES6", {
+ files: {
+ "/entry.js": `export * from 'fs'`,
+ "/test.js": /* js */ `
+ import assert from 'assert';
+ import * as fs from 'fs';
+ import * as fs2 from './out.js';
+ assert(fs, fs2);
+ `,
+ },
+ format: "esm",
+ platform: "node",
+ run: {
+ file: "/test.js",
+ },
+ });
+ itBundled("default/ExportWildcardFSNodeCommonJS", {
+ files: {
+ "/entry.js": `export * from 'fs'`,
+ "/test.js": /* js */ `
+ import assert from 'assert';
+ import * as fs from 'fs';
+ import * as fs2 from './out.js';
+ assert(fs, fs2);
+ `,
+ },
+ format: "cjs",
+ platform: "node",
+ run: {
+ file: "/test.js",
+ },
+ });
+ itBundled("default/MinifiedBundleES6", {
+ files: {
+ "/entry.js": /* js */ `
+ import {foo} from './a'
+ console.log(foo())
+ `,
+ "/a.js": /* js */ `
+ export function foo() {
+ console.log('call');
+ return 123
+ }
+ foo()
+ `,
+ },
+ minifySyntax: true,
+ minifyWhitespace: true,
+ minifyIdentifiers: true,
+ run: {
+ stdout: `
+ call
+ call
+ 123
+ `,
+ },
+ });
+ itBundled("default/MinifiedBundleCommonJS", {
+ files: {
+ "/entry.js": /* js */ `
+ const {foo} = require('./a')
+ console.log(foo(), JSON.stringify(require('./j.json')))
+ `,
+ "/a.js": /* js */ `
+ exports.foo = function() {
+ return 123
+ }
+ `,
+ "/j.json": `{"test": true}`,
+ },
+ minifySyntax: true,
+ minifyWhitespace: true,
+ minifyIdentifiers: true,
+ run: {
+ stdout: '123 {"test":true}',
+ },
+ });
+ itBundled("default/MinifiedBundleEndingWithImportantSemicolon", {
+ files: {
+ "/entry.js": `while(foo()); // This semicolon must not be stripped`,
+
+ "/test.js": /* js */ `
+ let i = 0;
+ globalThis.foo = () => {
+ console.log(i++);
+ return i === 1;
+ };
+ await import('./out.js')
+ `,
+ },
+ minifyWhitespace: true,
+ format: "iife",
+ run: {
+ file: "/test.js",
+ stdout: "0\n1",
+ },
+ });
+ itBundled("default/RuntimeNameCollisionNoBundle", {
+ files: {
+ "/entry.js": /* js */ `
+ function __require() { return 123 }
+ console.log(__require())
+ `,
+ },
+ mode: "transform",
+ run: {
+ stdout: "123",
+ },
+ });
+ itBundled("default/TopLevelReturnForbiddenImport", {
+ files: {
+ "/entry.js": /* js */ `
+ return
+ import 'foo'
+ `,
+ },
+ mode: "transform",
+ bundleErrors: {
+ "/entry.js": ["Top-level return cannot be used inside an ECMAScript module"],
+ },
+ });
+ itBundled("default/TopLevelReturnForbiddenExport", {
+ files: {
+ "/entry.js": /* js */ `
+ return
+ export var foo
+ `,
+ },
+ mode: "transform",
+ bundleErrors: {
+ "/entry.js": ["Top-level return cannot be used inside an ECMAScript module"],
+ },
+ });
+ itBundled("default/TopLevelReturnForbiddenTLA", {
+ files: {
+ "/entry.js": `return await foo`,
+ },
+ mode: "transform",
+ bundleErrors: {
+ "/entry.js": ["Top-level return cannot be used inside an ECMAScript module"],
+ },
+ });
+ itBundled("default/ThisOutsideFunctionRenamedToExports", {
+ files: {
+ "/entry.js": /* js */ `
+ console.log(this)
+ console.log((x = this) => this)
+ console.log({x: this})
+ console.log(class extends this.foo {})
+ console.log(class { [this.foo] })
+ console.log(class { [this.foo]() {} })
+ console.log(class { static [this.foo] })
+ console.log(class { static [this.foo]() {} })
+ `,
+ },
+ onAfterBundle(api) {
+ if (api.readFile("/out.js").includes("this")) {
+ throw new Error("All cases of `this` should have been rewritten to `exports`");
+ }
+ },
+ });
+ itBundled("default/ThisOutsideFunctionNotRenamed", {
+ files: {
+ "/entry.js": /* js */ `
+ class C1 { foo = this };
+ class C2 { foo() { return this } };
+ class C3 { static foo = this };
+ class C4 { static foo() { return this } };
+
+ const c1 = new C1();
+ const c2 = new C2();
+ globalThis.assert(c1 === c1.foo, 'c1.foo');
+ globalThis.assert(c2 === c2.foo(), 'c2.foo()');
+ globalThis.assert(C3.foo === C3, 'C3.foo');
+ globalThis.assert(C4.foo() === C4, 'C4.foo()');
+ `,
+
+ "/test.js": /* js */ `
+ globalThis.assert = (await import('assert')).default;
+ import('./out.js')
+ `,
+ },
+ run: {
+ file: "/test.js",
+ },
+ });
+ itBundled("default/ThisInsideFunction", {
+ files: {
+ "/entry.js": /* js */ `
+ function foo(x = this) { return [x, this]; }
+ const objFoo = {
+ foo(x = this) { return [x, this]; }
+ }
+ class Foo {
+ x = this
+ static z = 456;
+ static y = this.z;
+ foo(x = this) { return [x, this]; }
+ static bar(x = this) { return [x, this]; }
+ }
+
+ assert.deepEqual(foo('bun'), ['bun', undefined]);
+ assert.deepEqual(foo.call('this'), ['this', 'this']);
+ assert.deepEqual(foo.call('this', 'bun'), ['bun', 'this']);
+ assert.deepEqual(objFoo.foo('bun'), ['bun', objFoo]);
+ assert.deepEqual(objFoo.foo(), [objFoo, objFoo]);
+ const fooInstance = new Foo();
+ assert(fooInstance.x === fooInstance, 'Foo#x');
+ assert(Foo.y === 456, 'Foo.y');
+ assert.deepEqual(Foo.bar('bun'), ['bun', Foo]);
+ assert.deepEqual(Foo.bar(), [Foo, Foo]);
+ assert.deepEqual(fooInstance.foo(), [fooInstance, fooInstance]);
+ assert.deepEqual(fooInstance.foo('bun'), ['bun', fooInstance]);
+
+ if (nested) {
+ function bar(x = this) { return [x, this]; }
+ const objBar = {
+ foo(x = this) { return [x, this]; }
+ }
+ class Bar {
+ x = this
+ static z = 456;
+ static y = this.z
+ foo(x = this) { return [x, this]; }
+ static bar(x = this) { return [x, this]; }
+ }
+
+ assert.deepEqual(bar('bun'), ['bun', undefined]);
+ assert.deepEqual(bar.call('this'), ['this', 'this']);
+ assert.deepEqual(bar.call('this', 'bun'), ['bun', 'this']);
+ assert.deepEqual(objBar.foo('bun'), ['bun', objBar]);
+ assert.deepEqual(objBar.foo(), [objBar, objBar]);
+ const barInstance = new Bar();
+ assert(barInstance.x === barInstance, 'Bar#x');
+ assert(Bar.y === 456, 'Bar.y');
+ assert.deepEqual(Bar.bar('bun'), ['bun', Bar]);
+ assert.deepEqual(Bar.bar(), [Bar, Bar]);
+ assert.deepEqual(barInstance.foo(), [barInstance, barInstance]);
+ assert.deepEqual(barInstance.foo('bun'), ['bun', barInstance]);
+ }
+ `,
+
+ "/test.js": /* js */ `
+ globalThis.nested = true;
+ globalThis.assert = (await import('assert')).default;
+ import('./out')
+ `,
+ },
+ run: {
+ file: "/test.js",
+ },
+ });
+ itBundled("default/ThisWithES6Syntax", {
+ files: {
+ "/entry.js": /* js */ `
+ import './cjs'
+
+ import './es6-import-stmt'
+ import './es6-import-assign'
+ import './es6-import-dynamic'
+ import './es6-import-meta'
+ import './es6-expr-import-dynamic'
+ import './es6-expr-import-meta'
+
+ import './es6-export-variable'
+ import './es6-export-function'
+ import './es6-export-async-function'
+ import './es6-export-enum'
+ import './es6-export-const-enum'
+ import './es6-export-module'
+ import './es6-export-namespace'
+ import './es6-export-class'
+ import './es6-export-abstract-class'
+ import './es6-export-default'
+ import './es6-export-clause'
+ import './es6-export-clause-from'
+ import './es6-export-star'
+ import './es6-export-star-as'
+ import './es6-export-assign'
+ import './es6-export-import-assign'
+
+ import './es6-ns-export-variable'
+ import './es6-ns-export-function'
+ import './es6-ns-export-async-function'
+ import './es6-ns-export-enum'
+ import './es6-ns-export-const-enum'
+ import './es6-ns-export-module'
+ import './es6-ns-export-namespace'
+ import './es6-ns-export-class'
+ import './es6-ns-export-abstract-class'
+ `,
+ "/dummy.js": `export const dummy = 123`,
+ "/cjs.js": `console.log(JSON.stringify(this))`,
+ "/es6-import-stmt.js": `import './dummy'; console.log(JSON.stringify(this))`,
+ "/es6-import-assign.ts": `import x = require('./dummy'); console.log(JSON.stringify(this))`,
+ "/es6-import-dynamic.js": `import('./dummy'); console.log(JSON.stringify(this))`,
+ "/es6-import-meta.js": `import.meta; console.log(JSON.stringify(this))`,
+ "/es6-expr-import-dynamic.js": `(import('./dummy')); console.log(JSON.stringify(this))`,
+ "/es6-expr-import-meta.js": `(import.meta); console.log(JSON.stringify(this))`,
+ "/es6-export-variable.js": `export const foo = 123; console.log(JSON.stringify(this))`,
+ "/es6-export-function.js": `export function foo() {} console.log(JSON.stringify(this))`,
+ "/es6-export-async-function.js": `export async function foo() {} console.log(JSON.stringify(this))`,
+ "/es6-export-enum.ts": `export enum Foo {} console.log(JSON.stringify(this))`,
+ "/es6-export-const-enum.ts": `export const enum Foo {} console.log(JSON.stringify(this))`,
+ "/es6-export-module.ts": `export module Foo {} console.log(JSON.stringify(this))`,
+ "/es6-export-namespace.ts": `export namespace Foo {} console.log(JSON.stringify(this))`,
+ "/es6-export-class.js": `export class Foo {} console.log(JSON.stringify(this))`,
+ "/es6-export-abstract-class.ts": `export abstract class Foo {} console.log(JSON.stringify(this))`,
+ "/es6-export-default.js": `export default 123; console.log(JSON.stringify(this))`,
+ "/es6-export-clause.js": `export {}; console.log(JSON.stringify(this))`,
+ "/es6-export-clause-from.js": `export {} from './dummy'; console.log(JSON.stringify(this))`,
+ "/es6-export-star.js": `export * from './dummy'; console.log(JSON.stringify(this))`,
+ "/es6-export-star-as.js": `export * as ns from './dummy'; console.log(JSON.stringify(this))`,
+ "/es6-export-assign.ts": `export = 123; console.log(JSON.stringify(this))`,
+ "/es6-export-import-assign.ts": `export import x = require('./dummy'); console.log(JSON.stringify(this))`,
+ "/es6-ns-export-variable.ts": `namespace ns { export const foo = 123; } console.log(JSON.stringify(this))`,
+ "/es6-ns-export-function.ts": `namespace ns { export function foo() {} } console.log(JSON.stringify(this))`,
+ "/es6-ns-export-async-function.ts": `namespace ns { export async function foo() {} } console.log(JSON.stringify(this))`,
+ "/es6-ns-export-enum.ts": `namespace ns { export enum Foo {} } console.log(JSON.stringify(this))`,
+ "/es6-ns-export-const-enum.ts": `namespace ns { export const enum Foo {} } console.log(JSON.stringify(this))`,
+ "/es6-ns-export-module.ts": `namespace ns { export module Foo {} } console.log(JSON.stringify(this))`,
+ "/es6-ns-export-namespace.ts": `namespace ns { export namespace Foo {} } console.log(JSON.stringify(this))`,
+ "/es6-ns-export-class.ts": `namespace ns { export class Foo {} } console.log(JSON.stringify(this))`,
+ "/es6-ns-export-abstract-class.ts": `namespace ns { export abstract class Foo {} } console.log(JSON.stringify(this))`,
+ },
+ run: {
+ "stdout":
+ "{}\n{}\n{}\n{}\nundefined\n{}\nundefined\nundefined\nundefined\nundefined\nundefined\nundefined\nundefined\nundefined\nundefined\nundefined\nundefined\nundefined\nundefined\nundefined\nundefined\n{}\nundefined\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}",
+ },
+ });
+ itBundled("default/ArrowFnScope", {
+ // TODO: MANUAL CHECK: make sure the snapshot we use works.
+ files: {
+ "/entry.js": /* js */ `
+ tests = {
+ 0: ((x = y => x + y, y) => x + y),
+ 1: ((y, x = y => x + y) => x + y),
+ 2: ((x = (y = z => x + y + z, z) => x + y + z, y, z) => x + y + z),
+ 3: ((y, z, x = (z, y = z => x + y + z) => x + y + z) => x + y + z),
+ 4: ((x = y => x + y, y), x + y),
+ 5: ((y, x = y => x + y), x + y),
+ 6: ((x = (y = z => x + y + z, z) => x + y + z, y, z), x + y + z),
+ 7: ((y, z, x = (z, y = z => x + y + z) => x + y + z), x + y + z),
+ };
+ `,
+ },
+ minifyIdentifiers: true,
+ });
+ itBundled("default/SwitchScopeNoBundle", {
+ files: {
+ "/entry.js": /* js */ `
+ switch (foo) { default: var foo }
+ switch (bar) { default: let bar }
+ `,
+ },
+ minifyIdentifiers: true,
+ mode: "transform",
+ onAfterBundle(api) {
+ assert(!api.readFile("/out.js").includes("foo"), 'bundle shouldnt include "foo"');
+ assert(!api.readFile("/out.js").includes("let bar"), 'bundle shouldnt include "let bar"');
+ assert(!api.readFile("/out.js").includes("var bar"), 'bundle shouldnt include "var bar"');
+ },
+ run: {
+ error: "ReferenceError: Can't find variable: bar",
+ },
+ });
+ itBundled("default/ArgumentDefaultValueScopeNoBundle", {
+ files: {
+ "/entry.js": /* js */ `
+ export function a(x = foo) { var foo; return x }
+ export class b { fn(x = foo) { var foo; return x } }
+ export let c = [
+ function(x = foo) { var foo; return x },
+ (x = foo) => { var foo; return x },
+ { fn(x = foo) { var foo; return x }},
+ class { fn(x = foo) { var foo; return x }},
+ ]
+ `,
+ },
+ onAfterBundle(api) {
+ assert(
+ [...api.readFile("/out.js").matchAll(/= *foo/g)].length === 6,
+ 'foo default argument value should not have been replaced (expected to see exactly 6 instances of "= foo")',
+ );
+ },
+ minifyIdentifiers: true,
+ mode: "transform",
+ });
+ itBundled("default/ArgumentsSpecialCaseNoBundle", {
+ files: {
+ "/entry.js": /* js */ `
+ (async() => {
+ var arguments = 'var';
+
+ const f1 = function foo(x = arguments) { return [x, arguments] }
+ const f2 = (function(x = arguments) { return [x, arguments] });
+ const o1 = ({foo(x = arguments) { return [x, arguments] }});
+ const C1 = class Foo { foo(x = arguments) { return [x, arguments] } }
+ const C2 = (class { foo(x = arguments) { return [x, arguments] } });
+
+ const f3 = function foo(x = arguments) { var arguments; return [x, arguments] }
+ const f4 = (function(x = arguments) { var arguments; return [x, arguments] });
+ const o2 = ({foo(x = arguments) { var arguments; return [x, arguments] }});
+
+ console.log('marker');
+
+ const a1 = (x => [x, arguments]);
+ const a2 = (() => [arguments]);
+ const a3 = (async () => [arguments]);
+ const a4 = ((x = arguments) => [x, arguments]);
+ const a5 = (async (x = arguments) => [x, arguments]);
+
+ const a6 = x => [x, arguments];
+ const a7 = () => [arguments];
+ const a8 = async () => [arguments];
+ const a9 = (x = arguments) => [x, arguments];
+ const a10 = async (x = arguments) => [x, arguments];
+
+ const a11 = (x => { return [x, arguments] });
+ const a12 = (() => { return [arguments] });
+ const a13 = (async () => { return [arguments] });
+ const a14 = ((x = arguments) => { return [x, arguments] });
+ const a15 = (async (x = arguments) => { return [x, arguments] });
+
+ const a16 = x => { return [x, arguments] };
+ const a17 = () => { return [arguments] };
+ const a18 = async () => { return [arguments] };
+ const a19 = (x = arguments) => { return [x, arguments] };
+ const a20 = async (x = arguments) => { return [x, arguments] };
+
+ // assertions:
+ // we need this helper function to get "Arguments" objects, though this only applies for tests using v8
+ const argumentsFor = new Function('return arguments;');
+ const assert = require('assert');
+ assert.deepEqual(f1(), [argumentsFor(), argumentsFor()], 'f1()');
+ assert.deepEqual(f1(1), [1, argumentsFor(1)], 'f1(1)');
+ assert.deepEqual(f2(), [argumentsFor(), argumentsFor()], 'f2()');
+ assert.deepEqual(f2(1), [1, argumentsFor(1)], 'f2(1)');
+ assert.deepEqual(f3(), [argumentsFor(), argumentsFor()], 'f3()');
+ assert.deepEqual(f3(1), [1, argumentsFor(1)], 'f3(1)');
+ assert.deepEqual(o1.foo(), [argumentsFor(), argumentsFor()], 'o1.foo()');
+ assert.deepEqual(o1.foo(1), [1, argumentsFor(1)], 'o1.foo(1)');
+ assert.deepEqual(o2.foo(), [argumentsFor(), argumentsFor()], 'o2.foo()');
+ assert.deepEqual(o2.foo(1), [1, argumentsFor(1)], 'o2.foo(1)');
+ assert.deepEqual(new C1().foo(), [argumentsFor(), argumentsFor()], 'C1#foo()');
+ assert.deepEqual(new C1().foo(1), [1, argumentsFor(1)], 'C1#foo(1)');
+ assert.deepEqual(new C2().foo(), [argumentsFor(), argumentsFor()], 'C2#foo()');
+ assert.deepEqual(new C2().foo(1), [1, argumentsFor(1)], 'C2#foo(1)');
+ assert.deepEqual(a1(), [undefined, 'var'], 'a1()');
+ assert.deepEqual(a1(1), [1, 'var'], 'a1(1)');
+ assert.deepEqual(a2(), ['var'], 'a2()');
+ assert.deepEqual(await a3(), ['var'], 'a3()');
+ assert.deepEqual(a4(), ['var', 'var'], 'a4()');
+ assert.deepEqual(a4(1), [1, 'var'], 'a4(1)');
+ assert.deepEqual(await a5(), ['var', 'var'], 'a5()');
+ assert.deepEqual(await a5(1), [1, 'var'], 'a5(1)');
+ assert.deepEqual(a6(), [undefined, 'var'], 'a6()');
+ assert.deepEqual(a6(1), [1, 'var'], 'a6(1)');
+ assert.deepEqual(a7(), ['var'], 'a7()');
+ assert.deepEqual(await a8(), ['var'], 'a8()');
+ assert.deepEqual(a9(), ['var', 'var'], 'a9()');
+ assert.deepEqual(a9(1), [1, 'var'], 'a9(1)');
+ assert.deepEqual(await a10(), ['var', 'var'], 'a10()');
+ assert.deepEqual(await a10(1), [1, 'var'], 'a10(1)');
+ assert.deepEqual(a11(), [undefined, 'var'], 'a11()');
+ assert.deepEqual(a11(1), [1, 'var'], 'a11(1)');
+ assert.deepEqual(a12(), ['var'], 'a12()');
+ assert.deepEqual(await a13(), ['var'], 'a13()');
+ assert.deepEqual(a14(), ['var', 'var'], 'a14()');
+ assert.deepEqual(a14(1), [1, 'var'], 'a14(1)');
+ assert.deepEqual(await a15(), ['var', 'var'], 'a15()');
+ assert.deepEqual(await a15(1), [1, 'var'], 'a15(1)');
+ assert.deepEqual(a16(), [undefined, 'var'], 'a16()');
+ assert.deepEqual(a16(1), [1, 'var'], 'a16(1)');
+ assert.deepEqual(a17(), ['var'], 'a17()');
+ assert.deepEqual(await a18(), ['var'], 'a18()');
+ assert.deepEqual(a19(), ['var', 'var'], 'a19()');
+ assert.deepEqual(a19(1), [1, 'var'], 'a19(1)');
+ assert.deepEqual(await a20(), ['var', 'var'], 'a20()');
+ assert.deepEqual(await a20(1), [1, 'var'], 'a20(1)');
+ })();
+ `,
+ },
+ format: "cjs",
+ outfile: "/out.js",
+ minifyIdentifiers: true,
+ mode: "transform",
+ run: {
+ // TODO: use bun here after https://github.com/oven-sh/bun/issues/1724 is fixed
+ runtime: "node",
+ },
+ });
+ itBundled("default/WithStatementTaintingNoBundle", {
+ // TODO: MANUAL CHECK: make sure the snapshot we use works.
+ files: {
+ "/entry.js": /* js */ `
+ (() => {
+ let local = 1
+ let outer = 2
+ let outerDead = 3
+ with ({}) {
+ var hoisted = 4
+ let local = 5
+ hoisted++
+ local++
+ if (1) outer++
+ if (0) outerDead++
+ }
+ if (1) {
+ hoisted++
+ local++
+ outer++
+ outerDead++
+ }
+ })()
+ `,
+ },
+ format: "iife",
+ minifyIdentifiers: true,
+ mode: "transform",
+ });
+ itBundled("default/DirectEvalTaintingNoBundle", {
+ files: {
+ "/entry.js": /* js */ `
+ function test1() {
+ let shouldNotBeRenamed1 = 1;
+ function add(first, second) {
+ let renameMe = 1;
+ return first + second;
+ }
+ eval('add(1, 2)')
+ }
+
+ function test2() {
+ let renameMe1 = 1;
+ function add(first, second) {
+ let renameMe2 = 1;
+ return first + second
+ }
+ (0, eval)('add(1, 2)')
+ }
+
+ function test3() {
+ let renameMe1 = 1;
+ function add(first, second) {
+ let renameMe2 = 1;
+ return first + second
+ }
+ }
+
+ function test4(eval) {
+ let shouldNotBeRenamed2 = 1;
+ function add(first, second) {
+ let renameMe1 = 1;
+ return first + second
+ }
+ eval('add(1, 2)')
+ }
+
+ function test5() {
+ let shouldNotBeRenamed3 = 1;
+ function containsDirectEval() { eval() }
+ if (true) { var shouldNotBeRenamed4 }
+ }
+ `,
+ },
+ minifyIdentifiers: true,
+ mode: "transform",
+ format: "cjs",
+ onAfterBundle(api) {
+ const text = api.readFile("/out.js");
+ assert(text.includes("shouldNotBeRenamed1"), "Should not have renamed `shouldNotBeRenamed1`");
+ assert(text.includes("shouldNotBeRenamed2"), "Should not have renamed `shouldNotBeRenamed2`");
+ assert(text.includes("shouldNotBeRenamed3"), "Should not have renamed `shouldNotBeRenamed3`");
+ assert(text.includes("shouldNotBeRenamed4"), "Should not have renamed `shouldNotBeRenamed4`");
+ assert(!text.includes("renameMe"), "Should have renamed all `renameMe` variabled");
+ },
+ });
+ itBundled("default/ImportReExportES6Issue149", {
+ files: {
+ "/app.jsx": /* jsx */ `
+ import { p as Part, h, render } from './import';
+ import { Internal } from './in2';
+ const App = () => <Part> <Internal /> T </Part>;
+ render(<App />, 'a dom node');
+ `,
+ "/in2.jsx": /* jsx */ `
+ import { p as Part, h } from './import';
+ export const Internal = () => <Part> Test 2 </Part>;
+ `,
+ "/import.js": /* js */ `
+ import { h, render } from 'preact';
+ export const p = "p";
+ export { h, render }
+ `,
+ },
+ runtimeFiles: {
+ "/node_modules/preact/index.js": /* js */ `
+ export const p = 'part';
+ export const h = () => 'preact element';
+ export const render = (jsx) => {
+ if (jsx !== 'preact element') {
+ throw new Error('Test failed, is bun is applying automatic jsx?');
+ }
+ };
+ `,
+ },
+ jsx: {
+ factory: "h",
+ },
+ external: ["preact"],
+ run: true,
+ });
+ itBundled("default/ExternalModuleExclusionPackage", {
+ files: {
+ "/index.js": /* js */ `
+ import { S3 } from 'aws-sdk';
+ import { DocumentClient } from 'aws-sdk/clients/dynamodb';
+ export const s3 = new S3();
+ export const dynamodb = new DocumentClient();
+ `,
+ },
+ external: ["aws-sdk"],
+ });
+ itBundled("default/ExternalModuleExclusionScopedPackage", {
+ files: {
+ "/index.js": /* js */ `
+ import '@a1'
+ import '@a1/a2'
+ import '@a1-a2'
+
+ import '@b1'
+ import '@b1/b2'
+ import '@b1/b2/b3'
+ import '@b1/b2-b3'
+
+ import '@c1'
+ import '@c1/c2'
+ import '@c1/c2/c3'
+ import '@c1/c2/c3/c4'
+ import '@c1/c2/c3-c4'
+ `,
+ },
+ external: ["@a1", "@b1/b2", "@c1/c2/c3"],
+ bundleErrors: {
+ "/index.js": [
+ `Could not resolve: "@a1-a2". Maybe you need to "bun install"?`,
+ `Could not resolve: "@b1". Maybe you need to "bun install"?`,
+ `Could not resolve: "@b1/b2-b3". Maybe you need to "bun install"?`,
+ `Could not resolve: "@c1". Maybe you need to "bun install"?`,
+ `Could not resolve: "@c1/c2". Maybe you need to "bun install"?`,
+ `Could not resolve: "@c1/c2/c3-c4". Maybe you need to "bun install"?`,
+ ],
+ },
+ });
+ itBundled("default/ScopedExternalModuleExclusion", {
+ files: {
+ "/index.js": /* js */ `
+ import { Foo } from '@scope/foo';
+ import { Bar } from '@scope/foo/bar';
+ export const foo = new Foo();
+ export const bar = new Bar();
+ `,
+ },
+ external: ["@scope/foo"],
+ });
+ itBundled("default/ExternalModuleExclusionRelativePath", {
+ files: {
+ "/Users/user/project/src/index.js": `import './nested/folder/test'`,
+ "/Users/user/project/src/nested/folder/test.js": /* js */ `
+ import foo from './foo.js'
+ import out from '../../../out/in-out-dir.js'
+ import sha256 from '../../sha256.min.js'
+ import config from '/api/config?a=1&b=2'
+ console.log(foo, out, sha256, config)
+ `,
+ },
+ outdir: "/Users/user/project/out/",
+ external: [
+ "{{root}}/Users/user/project/out/in-out-dir.js",
+ "{{root}}/Users/user/project/src/nested/folder/foo.js",
+ "{{root}}/Users/user/project/src/sha256.min.js",
+ "/api/config?a=1&b=2",
+ ],
+ onAfterBundle(api) {
+ const file = api.readFile("/Users/user/project/out/index.js");
+ const imports = new Bun.Transpiler().scanImports(file);
+ expect(imports).toStrictEqual([
+ { kind: "import-statement", path: "../src/nested/folder/foo.js" },
+ { kind: "import-statement", path: "./in-out-dir.js" },
+ { kind: "import-statement", path: "../src/sha256.min.js" },
+ { kind: "import-statement", path: "/api/config?a=1&b=2" },
+ ]);
+ },
+ });
+ itBundled("default/ImportWithHashInPath", {
+ files: {
+ "/entry.js": /* js */ `
+ import foo from './file#foo.txt'
+ import bar from './file#bar.txt'
+ console.log(foo, bar)
+ `,
+ "/file#foo.txt": `foo`,
+ "/file#bar.txt": `bar`,
+ },
+ run: {
+ stdout: "foo bar",
+ },
+ });
+ itBundled("default/ImportWithHashParameter", {
+ files: {
+ "/entry.js": /* js */ `
+ // Each of these should have a separate identity (i.e. end up in the output file twice)
+ import foo from './file.txt#foo'
+ import bar from './file.txt#bar'
+ console.log(foo, bar)
+ `,
+ "/file.txt": `This is some text`,
+ },
+ run: {
+ stdout: "This is some text This is some text",
+ },
+ });
+ itBundled("default/ImportWithQueryParameter", {
+ files: {
+ "/entry.js": /* js */ `
+ // Each of these should have a separate identity (i.e. end up in the output file twice)
+ import foo from './file.txt?foo'
+ import bar from './file.txt?bar'
+ console.log(foo, bar)
+ `,
+ "/file.txt": `This is some text`,
+ },
+ run: {
+ stdout: "This is some text This is some text",
+ },
+ });
+ itBundled("default/ImportAbsPathWithQueryParameter", {
+ files: {
+ "/Users/user/project/entry.js": /* js */ `
+ // Each of these should have a separate identity (i.e. end up in the output file twice)
+ import foo from '{{root}}/Users/user/project/file.txt?foo'
+ import bar from '{{root}}/Users/user/project/file.txt#bar'
+ console.log(foo, bar)
+ `,
+ "/Users/user/project/file.txt": `This is some text`,
+ },
+ run: {
+ stdout: "This is some text This is some text",
+ },
+ });
+ itBundled("default/ImportAbsPathAsFile", {
+ files: {
+ "/Users/user/project/entry.js": /* js */ `
+ import pkg from '{{root}}/Users/user/project/node_modules/pkg/index'
+ console.log(pkg)
+ `,
+ "/Users/user/project/node_modules/pkg/index.js": `export default 123`,
+ },
+ run: {
+ stdout: "123",
+ },
+ });
+ itBundled("default/ImportAbsPathAsDirUnix", {
+ files: {
+ "/Users/user/project/entry.js": /* js */ `
+ import pkg from '{{root}}/Users/user/project/node_modules/pkg'
+ console.log(pkg)
+ `,
+ "/Users/user/project/node_modules/pkg/index.js": `export default 123`,
+ },
+ run: {
+ stdout: "123",
+ },
+ });
+ // itBundled("default/ImportBackslashNormalization", {
+ // files: {
+ // "/Users/user/project/entry.js": /* js */ `
+ // import pkg from '{{root}}\\\\Users\\\\user\\\\project\\\\node_modules\\\\pkg'
+ // console.log(pkg)
+ // `,
+ // "/Users/user/project/node_modules/pkg/index.js": `export default 123`,
+ // },
+ // run: {
+ // stdout: "123",
+ // },
+ // });
+ itBundled("default/AutoExternal", {
+ files: {
+ "/entry.js": /* js */ `
+ // These URLs should be external automatically
+ import "http://example.com/code.js";
+ import "https://example.com/code.js";
+ import "//example.com/code.js";
+ import "data:application/javascript;base64,ZXhwb3J0IGRlZmF1bHQgMTIz";
+ `,
+ },
+ onAfterBundle(api) {
+ const file = api.readFile("/out.js");
+ const imports = new Bun.Transpiler().scanImports(file);
+ expect(imports).toStrictEqual([
+ { kind: "import-statement", path: "http://example.com/code.js" },
+ { kind: "import-statement", path: "https://example.com/code.js" },
+ { kind: "import-statement", path: "//example.com/code.js" },
+ { kind: "import-statement", path: "data:application/javascript;base64,ZXhwb3J0IGRlZmF1bHQgMTIz" },
+ ]);
+ },
+ });
+ itBundled("default/AutoExternalNode", {
+ files: {
+ "/entry.js": /* js */ `
+ // These URLs should be external automatically
+ import fs from "node:fs/promises";
+ fs.readFile();
+
+ // This should be external and should be tree-shaken because it's side-effect free
+ import "node:path";
+
+ // This should be external too, but shouldn't be tree-shaken because it could be a run-time error
+ import "node:what-is-this";
+ `,
+ },
+ platform: "node",
+ onAfterBundle(api) {
+ const file = api.readFile("/out.js");
+ const imports = new Bun.Transpiler().scanImports(file);
+ expect(imports).toStrictEqual([
+ { kind: "import-statement", path: "node:fs/promises" },
+ { kind: "import-statement", path: "node:what-is-this" },
+ ]);
+ },
+ });
+ itBundled("default/ExternalWithWildcard", {
+ files: {
+ "/entry.js": /* js */ `
+ // Should match
+ import "/assets/images/test.jpg";
+ import "/dir/x/file.gif";
+ import "/dir//file.gif";
+ import "./file.png";
+
+ // Should not match
+ import "/sassets/images/test.jpg";
+ import "/dir/file.gif";
+ import "./file.ping";
+ `,
+ },
+ external: ["/assets/*", "*.png", "/dir/*/file.gif"],
+ bundleErrors: {
+ "/entry.js": [
+ 'Could not resolve "/sassets/images/test.jpg"',
+ 'Could not resolve "/dir/file.gif"',
+ 'Could not resolve "./file.ping"',
+ ],
+ },
+ });
+ itBundled("default/ExternalWildcardDoesNotMatchEntryPoint", {
+ skipOnEsbuild: true,
+ files: {
+ "/entry.js": `import "foo"`,
+ },
+ external: ["*"],
+ });
+ itBundled("default/ManyEntryPoints", {
+ files: Object.fromEntries([
+ ["/shared.js", "export default 123"],
+ ...Array.from({ length: 40 }, (_, i) => [
+ `/e${String(i).padStart(2, "0")}.js`,
+ `import x from "./shared"; console.log(x)`,
+ ]),
+ ]),
+ entryPoints: Array.from({ length: 40 }, (_, i) => `/e${String(i).padStart(2, "0")}.js`),
+ });
+ itBundled("default/MinifyPrivateIdentifiersNoBundle", {
+ files: {
+ "/entry.js": /* js */ `
+ class Foo {
+ doNotRenameMe
+ #foo
+ foo = class {
+ #foo
+ #foo2
+ #bar
+ }
+ get #bar() {}
+ set #bar(x) {}
+ }
+ class Bar {
+ doNotRenameMe
+ #foo
+ foo = class {
+ #foo2
+ #foo
+ #bar
+ }
+ get #bar() {}
+ set #bar(x) {}
+ }
+ `,
+ },
+ minifyIdentifiers: true,
+ mode: "transform",
+ onAfterBundle(api) {
+ const text = api.readFile("/out.js");
+ assert(text.includes("doNotRenameMe"), "bundler should not have renamed `doNotRenameMe`");
+ assert(!text.includes("#foo"), "bundler should have renamed `#foo`");
+ },
+ });
+ // These labels should all share the same minified names
+ itBundled("default/MinifySiblingLabelsNoBundle", {
+ files: {
+ "/entry.js": /* js */ `
+ foo: {
+ bar: {
+ if (x) break bar
+ break foo
+ }
+ }
+ foo2: {
+ bar2: {
+ if (x) break bar2
+ break foo2
+ }
+ }
+ foo: {
+ bar: {
+ if (x) break bar
+ break foo
+ }
+ }
+ `,
+ },
+ minifyIdentifiers: true,
+ mode: "transform",
+ onAfterBundle(api) {
+ const text = api.readFile("/out.js");
+ const labels = [...text.matchAll(/([a-z0-9]+):/gi)].map(x => x[1]);
+ expect(labels).toStrictEqual([labels[0], labels[1], labels[0], labels[1], labels[0], labels[1]]);
+ },
+ });
+ itBundled("default/MinifyNestedLabelsNoBundle", {
+ files: {
+ "/entry.js": dedent`
+ L001:{L002:{L003:{L004:{L005:{L006:{L007:{L008:{L009:{L010:{L011:{L012:{L013:{L014:{L015:{L016:{console.log('a')
+ L017:{L018:{L019:{L020:{L021:{L022:{L023:{L024:{L025:{L026:{L027:{L028:{L029:{L030:{L031:{L032:{console.log('a')
+ L033:{L034:{L035:{L036:{L037:{L038:{L039:{L040:{L041:{L042:{L043:{L044:{L045:{L046:{L047:{L048:{console.log('a')
+ L049:{L050:{L051:{L052:{L053:{L054:{L055:{L056:{L057:{L058:{L059:{L060:{L061:{L062:{L063:{L064:{console.log('a')
+ L065:{L066:{L067:{L068:{L069:{L070:{L071:{L072:{L073:{L074:{L075:{L076:{L077:{L078:{L079:{L080:{console.log('a')
+ L081:{L082:{L083:{L084:{L085:{L086:{L087:{L088:{L089:{L090:{L091:{L092:{L093:{L094:{L095:{L096:{console.log('a')
+ L097:{L098:{L099:{L100:{L101:{L102:{L103:{L104:{L105:{L106:{L107:{L108:{L109:{L110:{L111:{L112:{console.log('a')
+ L113:{L114:{L115:{L116:{L117:{L118:{L119:{L120:{L121:{L122:{L123:{L124:{L125:{L126:{L127:{L128:{console.log('a')
+ L129:{L130:{L131:{L132:{L133:{L134:{L135:{L136:{L137:{L138:{L139:{L140:{L141:{L142:{L143:{L144:{console.log('a')
+ L145:{L146:{L147:{L148:{L149:{L150:{L151:{L152:{L153:{L154:{L155:{L156:{L157:{L158:{L159:{L160:{console.log('a')
+ L161:{L162:{L163:{L164:{L165:{L166:{L167:{L168:{L169:{L170:{L171:{L172:{L173:{L174:{L175:{L176:{console.log('a')
+ L177:{L178:{L179:{L180:{L181:{L182:{L183:{L184:{L185:{L186:{L187:{L188:{L189:{L190:{L191:{L192:{console.log('a')
+ L193:{L194:{L195:{L196:{L197:{L198:{L199:{L200:{L201:{L202:{L203:{L204:{L205:{L206:{L207:{L208:{console.log('a')
+ L209:{L210:{L211:{L212:{L213:{L214:{L215:{L216:{L217:{L218:{L219:{L220:{L221:{L222:{L223:{L224:{console.log('a')
+ L225:{L226:{L227:{L228:{L229:{L230:{L231:{L232:{L233:{L234:{L235:{L236:{L237:{L238:{L239:{L240:{console.log('a')
+ L241:{L242:{L243:{L244:{L245:{L246:{L247:{L248:{L249:{L250:{L251:{L252:{L253:{L254:{L255:{L256:{console.log('a')
+ L257:{L258:{L259:{L260:{L261:{L262:{L263:{L264:{L265:{L266:{L267:{L268:{L269:{L270:{L271:{L272:{console.log('a')
+ L273:{L274:{L275:{L276:{L277:{L278:{L279:{L280:{L281:{L282:{L283:{L284:{L285:{L286:{L287:{L288:{console.log('a')
+ L289:{L290:{L291:{L292:{L293:{L294:{L295:{L296:{L297:{L298:{L299:{L300:{L301:{L302:{L303:{L304:{console.log('a')
+ L305:{L306:{L307:{L308:{L309:{L310:{L311:{L312:{L313:{L314:{L315:{L316:{L317:{L318:{L319:{L320:{console.log('a')
+ L321:{L322:{L323:{L324:{L325:{L326:{L327:{L328:{L329:{L330:{L331:{L332:{L333:{}}}}}}}}}}}}}}}}}}console.log('a')
+ }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}console.log('a')
+ }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}console.log('a')
+ }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}console.log('a')
+ }}}}}}}}}}}}}}}}}}}}}}}}}}}
+ `,
+ },
+ minifyWhitespace: true,
+ minifyIdentifiers: true,
+ minifySyntax: true,
+ mode: "transform",
+ });
+ itBundled("default/ExportsAndModuleFormatCommonJS", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as foo from './foo/test'
+ import * as bar from './bar/test'
+ console.log(JSON.stringify([exports, module.exports, foo, bar]), exports === module.exports)
+ `,
+ "/foo/test.js": `export let foo = 123`,
+ "/bar/test.js": `export let bar = 123`,
+ },
+ format: "cjs",
+ run: {
+ stdout: '[{},{},{"foo":123},{"bar":123}] true',
+ },
+ });
+ itBundled("default/MinifiedExportsAndModuleFormatCommonJS", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as foo from './foo/test'
+ import * as bar from './bar/test'
+ console.log(JSON.stringify([exports, module.exports, foo, bar]), exports === module.exports)
+ `,
+ "/foo/test.js": `export let foo = 123`,
+ "/bar/test.js": `export let bar = 123`,
+ },
+ minifyIdentifiers: true,
+ format: "cjs",
+ run: {
+ stdout: '[{},{},{"foo":123},{"bar":123}] true',
+ },
+ });
+ itBundled("default/EmptyExportClauseBundleAsCommonJSIssue910", {
+ files: {
+ "/entry.js": `console.log(JSON.stringify(require('./types.mjs')))`,
+ "/types.mjs": `export {}`,
+ },
+ format: "cjs",
+ run: {
+ stdout: "{}",
+ },
+ });
+ itBundled("default/UseStrictDirectiveMinifyNoBundle", {
+ files: {
+ "/entry.js": /* js */ `
+ 'use strict'
+ 'use loose'
+ a
+ b
+ `,
+ },
+ format: "iife",
+ minifySyntax: true,
+ minifyWhitespace: true,
+ mode: "transform",
+ onAfterBundle(api) {
+ assert(api.readFile("/out.js").includes('"use strict";'), '"use strict"; was emitted');
+ },
+ });
+ itBundled("default/UseStrictDirectiveBundleIssue1837", {
+ files: {
+ "/entry.js": /* js */ `
+ const p = require('./cjs').foo;
+ console.log(typeof p);
+ `,
+ "/cjs.js": /* js */ `
+ 'use strict'
+ exports.foo = process
+ `,
+ "/shims.js": /* js */ `
+ import { readFileSync } from 'fs'
+ export { readFileSync as process }
+ `,
+ },
+ inject: ["/shims.js"],
+ platform: "node",
+ run: {
+ stdout: "function",
+ },
+ });
+ itBundled("default/UseStrictDirectiveBundleIIFEIssue2264", {
+ files: {
+ "/entry.js": /* js */ `
+ 'use strict'
+ export let a = 1
+ `,
+ },
+ format: "iife",
+ onAfterBundle(api) {
+ assert(api.readFile("/out.js").includes('"use strict";'), '"use strict"; should be emitted');
+ },
+ });
+ itBundled("default/UseStrictDirectiveBundleCJSIssue2264", {
+ files: {
+ "/entry.js": /* js */ `
+ 'use strict'
+ export let a = 1
+ `,
+ },
+ format: "cjs",
+ onAfterBundle(api) {
+ assert(api.readFile("/out.js").includes('"use strict";'), '"use strict"; should be emitted');
+ },
+ });
+ itBundled("default/UseStrictDirectiveBundleESMIssue2264", {
+ files: {
+ "/entry.js": /* js */ `
+ 'use strict'
+ export let a = 1
+ `,
+ },
+ format: "esm",
+ onAfterBundle(api) {
+ assert(!api.readFile("/out.js").includes('"use strict";'), '"use strict"; should not be emitted');
+ },
+ });
+ // itBundled("default/NoOverwriteInputFileError", {
+ // files: {
+ // "/entry.js": `console.log(123)`,
+ // },
+ // outfile: "/entry.js",
+ // bundleErrors: {
+ // "/entry.js": ['Refusing to overwrite input file "entry.js" (use "--allow-overwrite" to allow this)'],
+ // },
+ // });
+ itBundled("default/DuplicateEntryPoint", {
+ files: {
+ "/entry.js": `console.log(123)`,
+ },
+ entryPoints: ["/entry.js", "/entry.js"],
+ run: {
+ file: "/out/entry.js",
+ stdout: "123",
+ },
+ });
+ itBundled("default/RelativeEntryPointError", {
+ files: {
+ "/entry.js": `console.log(123)`,
+ },
+ entryPointsRaw: ["entry"],
+ outfile: "/out.js",
+ bundleErrors: {
+ "<bun>": [`ModuleNotFound resolving "entry". Did you mean: "./entry"`],
+ },
+ });
+ itBundled("default/MultipleEntryPointsSameNameCollision", {
+ files: {
+ "/a/entry.js": `import {foo} from '../common.js'; console.log(foo)`,
+ "/b/entry.js": `import {foo} from '../common.js'; console.log(foo)`,
+ "/common.js": `export let foo = 123`,
+ },
+ entryPoints: ["./a/entry.js", "./b/entry.js"],
+ outdir: "/out/",
+ outputPaths: ["/out/a/entry.js", "/out/b/entry.js"],
+ });
+ itBundled("default/ReExportCommonJSAsES6", {
+ files: {
+ "/entry.js": `export {bar} from './foo'`,
+ "/foo.js": `exports.bar = 123`,
+
+ "/test.js": /* js */ `
+ import { bar } from './out';
+ console.log(bar);
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: "123",
+ },
+ });
+ itBundled("default/ReExportDefaultInternal", {
+ files: {
+ "/entry.js": /* js */ `
+ export {default as foo} from './foo'
+ export {default as bar} from './bar'
+ `,
+ "/foo.js": `export default 'foo'`,
+ "/bar.js": `export default 'bar'`,
+
+ "/test.js": /* js */ `
+ import { foo, bar } from './out';
+ console.log(foo, bar);
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: "foo bar",
+ },
+ });
+ itBundled("default/ReExportDefaultExternalES6", {
+ files: {
+ "/entry.js": /* js */ `
+ export {default as foo} from 'foo'
+ export {bar} from './bar'
+ `,
+ "/bar.js": `export {default as bar} from 'bar'`,
+ },
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ import { foo, bar } from './out';
+ console.log(foo, bar);
+ `,
+ "/node_modules/foo/index.js": /* js */ `
+ export default 'foo'
+ `,
+ "/node_modules/bar/index.js": /* js */ `
+ export default 'bar'
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: "foo bar",
+ },
+ format: "esm",
+ external: ["foo", "bar"],
+ });
+ itBundled("default/ReExportDefaultExternalCommonJS", {
+ files: {
+ "/entry.js": /* js */ `
+ export {default as foo} from 'foo'
+ export {bar} from './bar'
+ `,
+ "/bar.js": `export {default as bar} from 'bar'`,
+ },
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ const { foo, bar } = require('./out');
+ console.log(foo.default, bar.default);
+ `,
+ "/node_modules/foo/index.js": /* js */ `
+ module.exports = { default: 'foo' };
+ `,
+ "/node_modules/bar/index.js": /* js */ `
+ module.exports = { default: 'bar' };
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: "foo bar",
+ },
+ format: "cjs",
+ external: ["foo", "bar"],
+ });
+ itBundled("default/ReExportDefaultNoBundle", {
+ files: {
+ "/entry.js": /* js */ `
+ export {default as foo} from './foo'
+ export {default as bar} from './bar'
+ `,
+ },
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ import { foo, bar } from './out';
+ console.log(foo, bar);
+ `,
+ "/foo.js": /* js */ `
+ export default 'foo'
+ `,
+ "/bar.js": /* js */ `
+ export default 'bar'
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: "foo bar",
+ },
+ mode: "transform",
+ });
+ itBundled("default/ImportMetaCommonJS", {
+ files: {
+ "/entry.js": `console.log(import.meta.url, import.meta.path)`,
+ },
+ format: "cjs",
+ bundleWarnings: {
+ "/entry.js": [`"import.meta" is not available with the "cjs" output format and will be empty`],
+ },
+ run: {
+ stdout: "undefined undefined",
+ },
+ });
+ itBundled("default/ImportMetaES6", {
+ files: {
+ "/entry.js": `console.log(import.meta.url, import.meta.path)`,
+ },
+ format: "esm",
+ run: {
+ stdout: "url_here path_here",
+ bunArgs: ["--define", 'import.meta.url="url_here"', "--define", 'import.meta.path="path_here"'],
+ },
+ });
+ itBundled("default/ImportMetaNoBundle", {
+ files: {
+ "/entry.js": `console.log(import.meta.url, import.meta.path)`,
+ },
+ mode: "transform",
+ run: {
+ stdout: "url_here path_here",
+ bunArgs: ["--define", 'import.meta.url="url_here"', "--define", 'import.meta.path="path_here"'],
+ },
+ });
+ itBundled("default/LegalCommentsNone", {
+ files: {
+ "/entry.js": /* js */ `
+ import './a'
+ import './b'
+ import './c'
+ `,
+ "/a.js": `console.log('in a') //! Copyright notice 1`,
+ "/b.js": `console.log('in b') //! Copyright notice 1`,
+ "/c.js": `console.log('in c') //! Copyright notice 2`,
+ "/entry.css": /* css */ `
+ @import "./a.css";
+ @import "./b.css";
+ @import "./c.css";
+ `,
+ "/a.css": `a { zoom: 2 } /*! Copyright notice 1 */`,
+ "/b.css": `b { zoom: 2 } /*! Copyright notice 1 */`,
+ "/c.css": `c { zoom: 2 } /*! Copyright notice 2 */`,
+ },
+ outdir: "/out",
+ entryPoints: ["/entry.js", "/entry.css"],
+ legalComments: "none",
+ onAfterBundle(api) {
+ assert(!api.readFile("/out/entry.js").includes("Copyright notice"), "js should not contain copyright notice");
+ assert(!api.readFile("/out/entry.css").includes("Copyright notice"), "css should not contain copyright notice");
+ },
+ });
+ itBundled("default/LegalCommentsInline", {
+ files: {
+ "/entry.js": /* js */ `
+ // Normal Comment
+ import './a'
+ import './b'
+ import './c'
+ `,
+ "/a.js": `console.log('in a') //! Copyright notice 1`,
+ "/b.js": `console.log('in b') //! Copyright notice 1\n// Normal Comment`,
+ "/c.js": `console.log('in c') //! Copyright notice 2`,
+ "/entry.css": /* css */ `
+ /* Normal Comment */
+ @import "./a.css";
+ @import "./b.css";
+ @import "./c.css";
+ `,
+ "/a.css": `a { zoom: 2 } /*! Copyright notice 1 */`,
+ "/b.css": `b { zoom: 2 } /*! Copyright notice 1 */ /* Normal Comment */`,
+ "/c.css": `c { zoom: 2 } /*! Copyright notice 2 */`,
+ },
+ outdir: "/out",
+ entryPoints: ["/entry.js", "/entry.css"],
+ legalComments: "inline",
+ minifyWhitespace: true,
+ onAfterBundle(api) {
+ const entry = api.readFile("/out/entry.js");
+ assert(entry.match(/Copyright notice 1/g)?.length === 2, "js should contain copyright notice 1 twice");
+ assert(entry.match(/Copyright notice 2/g)?.length === 1, "js should contain copyright notice 2 once");
+ assert(!entry.includes("Normal Comment"), "js should not contain normal comments");
+
+ const entry2 = api.readFile("/out/entry.css");
+ assert(entry2.match(/Copyright notice 1/g)?.length === 2, "css should contain copyright notice 1 twice");
+ assert(entry2.match(/Copyright notice 2/g)?.length === 1, "css should contain copyright notice 2 once");
+ assert(!entry2.includes("Normal Comment"), "css should not contain normal comments");
+ },
+ });
+ itBundled("default/LegalCommentsEndOfFile", {
+ files: {
+ "/entry.js": /* js */ `
+ import './a'
+ import './b'
+ import './c'
+ `,
+ "/a.js": `console.log('in a') //! Copyright notice 1`,
+ "/b.js": `console.log('in b') //! Copyright notice 1`,
+ "/c.js": `console.log('in c') //! Copyright notice 2`,
+ "/entry.css": /* css */ `
+ @import "./a.css";
+ @import "./b.css";
+ @import "./c.css";
+ `,
+ "/a.css": `a { zoom: 2 } /*! Copyright notice 1 */`,
+ "/b.css": `b { zoom: 2 } /*! Copyright notice 1 */`,
+ "/c.css": `c { zoom: 2 } /*! Copyright notice 2 */`,
+ },
+ outdir: "/out",
+ entryPoints: ["/entry.js", "/entry.css"],
+ legalComments: "eof",
+ onAfterBundle(api) {
+ assert(
+ api
+ .readFile("/out/entry.js")
+ .trim()
+ .endsWith(
+ dedent`
+ //! Copyright notice 1
+ //! Copyright notice 2
+ `,
+ ),
+ 'js should end with "Copyright notice 1" and "Copyright notice 2", in that order. No duplicates.',
+ );
+ assert(
+ api
+ .readFile("/out/entry.css")
+ .trim()
+ .endsWith(
+ dedent`
+ /*! Copyright notice 1 */
+ /*! Copyright notice 2 */
+ `,
+ ),
+ 'css should end with "Copyright notice 1" and "Copyright notice 2", in that order. No duplicates.',
+ );
+ },
+ });
+ itBundled("default/LegalCommentsLinked", {
+ files: {
+ "/entry.js": /* js */ `
+ import './a'
+ import './b'
+ import './c'
+ `,
+ "/a.js": `console.log('in a') //! Copyright notice 1`,
+ "/b.js": `console.log('in b') //! Copyright notice 1`,
+ "/c.js": `console.log('in c') //! Copyright notice 2`,
+ "/entry.css": /* css */ `
+ @import "./a.css";
+ @import "./b.css";
+ @import "./c.css";
+ `,
+ "/a.css": `a { zoom: 2 } /*! Copyright notice 1 */`,
+ "/b.css": `b { zoom: 2 } /*! Copyright notice 1 */`,
+ "/c.css": `c { zoom: 2 } /*! Copyright notice 2 */`,
+ },
+ outdir: "/out",
+ entryPoints: ["/entry.js", "/entry.css"],
+ legalComments: "linked",
+ onAfterBundle(api) {
+ assert(
+ api.readFile("/out/entry.js").trim().endsWith(`/*! For license information please see entry.js.LEGAL.txt */`),
+ 'js should end with the exact text "/*! For license information please see entry.js.LEGAL.txt */"',
+ );
+ assert(
+ api.readFile("/out/entry.css").trim().endsWith(`/*! For license information please see entry.css.LEGAL.txt */`),
+ 'js should end with the exact text "/*! For license information please see entry.js.LEGAL.txt */"',
+ );
+ assert(
+ api.readFile("/out/entry.js.LEGAL.txt").trim() ===
+ dedent`
+ //! Copyright notice 1
+ //! Copyright notice 2
+ `,
+ );
+ assert(
+ api.readFile("/out/entry.css.LEGAL.txt").trim() ===
+ dedent`
+ /*! Copyright notice 1 */
+ /*! Copyright notice 2 */
+ `,
+ );
+ },
+ });
+ itBundled("default/LegalCommentsExternal", {
+ files: {
+ "/entry.js": /* js */ `
+ import './a'
+ import './b'
+ import './c'
+ `,
+ "/a.js": `console.log('in a') //! Copyright notice 1`,
+ "/b.js": `console.log('in b') //! Copyright notice 1`,
+ "/c.js": `console.log('in c') //! Copyright notice 2`,
+ "/entry.css": /* css */ `
+ @import "./a.css";
+ @import "./b.css";
+ @import "./c.css";
+ `,
+ "/a.css": `a { zoom: 2 } /*! Copyright notice 1 */`,
+ "/b.css": `b { zoom: 2 } /*! Copyright notice 1 */`,
+ "/c.css": `c { zoom: 2 } /*! Copyright notice 2 */`,
+ },
+ entryPoints: ["/entry.js", "/entry.css"],
+ legalComments: "external",
+ onAfterBundle(api) {
+ assert(!api.readFile("/out/entry.js").includes(`entry.js.LEGAL.txt`), "js should NOT mention legal information");
+ assert(
+ !api.readFile("/out/entry.css").includes(`entry.css.LEGAL.txt`),
+ "css should NOT mention legal information",
+ );
+ assert(
+ api.readFile("/out/entry.js.LEGAL.txt").trim() ===
+ dedent`
+ //! Copyright notice 1
+ //! Copyright notice 2
+ `,
+ );
+ assert(
+ api.readFile("/out/entry.css.LEGAL.txt").trim() ===
+ dedent`
+ /*! Copyright notice 1 */
+ /*! Copyright notice 2 */
+ `,
+ );
+ },
+ });
+ itBundled("default/LegalCommentsModifyIndent", {
+ files: {
+ "/entry.js": /* js */ `
+ export default () => {
+ /**
+ * @preserve
+ */
+ }
+ `,
+ "/entry.css": /* css */ `
+ @media (x: y) {
+ /**
+ * @preserve
+ */
+ z { zoom: 2 }
+ }
+ `,
+ },
+ outdir: "/out",
+ minifyWhitespace: true,
+ entryPoints: ["/entry.js", "/entry.css"],
+ legalComments: "inline",
+ onAfterBundle(api) {
+ assert(api.readFile("/out/entry.js").trim().includes("@preserve"), "js should include the @preserve comment");
+ assert(api.readFile("/out/entry.css").trim().includes("@preserve"), "css should include the @preserve comment");
+ },
+ });
+ itBundled("default/LegalCommentsAvoidSlashTagInline", {
+ files: {
+ "/entry.js": /* js */ `
+ //! <script>foo</script>
+ export let x
+ `,
+ "/entry.css": /* css */ `
+ /*! <style>foo</style> */
+ x { y: z }
+ `,
+ },
+ outdir: "/out",
+ entryPoints: ["/entry.js", "/entry.css"],
+ legalComments: "inline",
+ onAfterBundle(api) {
+ assert(api.readFile("/out/entry.js").trim().includes("<script>foo<\\/script>"), "js should have escaped comment");
+ assert(api.readFile("/out/entry.css").trim().includes("<style>foo<\\/style>"), "css should have escaped comment");
+ },
+ });
+ itBundled("default/LegalCommentsAvoidSlashTagEndOfFile", {
+ files: {
+ "/entry.js": /* js */ `
+ //! <script>foo</script>
+ export let x
+ `,
+ "/entry.css": /* css */ `
+ /*! <style>foo</style> */
+ x { y: z }
+ `,
+ },
+ outdir: "/out",
+ entryPoints: ["/entry.js", "/entry.css"],
+ legalComments: "eof",
+ onAfterBundle(api) {
+ assert(api.readFile("/out/entry.js").trim().includes("<script>foo<\\/script>"), "js should have escaped comment");
+ assert(api.readFile("/out/entry.css").trim().includes("<style>foo<\\/style>"), "css should have escaped comment");
+ },
+ });
+ itBundled("default/LegalCommentsAvoidSlashTagExternal", {
+ files: {
+ "/entry.js": /* js */ `
+ //! <script>foo</script>
+ export let x
+ `,
+ "/entry.css": /* css */ `
+ /*! <style>foo</style> */
+ x { y: z }
+ `,
+ },
+ outdir: "/out",
+ entryPoints: ["/entry.js", "/entry.css"],
+ legalComments: "external",
+ onAfterBundle(api) {
+ assert(
+ api.readFile("/out/entry.js.LEGAL.txt").trim().includes("<script>foo</script>"),
+ "js should NOT have escaped comment",
+ );
+ assert(
+ api.readFile("/out/entry.css.LEGAL.txt").trim().includes("<style>foo</style>"),
+ "css should NOT have escaped comment",
+ );
+ },
+ });
+ itBundled("default/LegalCommentsManyEndOfFile", {
+ files: {
+ "/project/entry.js": /* js */ `
+ import './a'
+ import './b'
+ import './c'
+ import 'some-pkg/js'
+ `,
+ "/project/a.js": /* js */ `
+ console.log('in a') //! Copyright notice 1
+ //! Duplicate comment
+ //! Duplicate comment
+ `,
+ "/project/b.js": /* js */ `
+ console.log('in b') //! Copyright notice 1
+ //! Duplicate comment
+ //! Duplicate comment
+ `,
+ "/project/c.js": /* js */ `
+ function foo() {
+ /*
+ * @license
+ * Copyright notice 2
+ */
+ console.log('in c')
+ // @preserve This is another comment
+ }
+ foo()
+ `,
+ "/project/node_modules/some-pkg/js/index.js": /* js */ `
+ import "some-other-pkg/js" //! (c) Good Software Corp
+ //! Duplicate third-party comment
+ //! Duplicate third-party comment
+ `,
+ "/project/node_modules/some-other-pkg/js/index.js": /* js */ `
+ function bar() {
+ /*
+ * @preserve
+ * (c) Evil Software Corp
+ */
+ console.log('some-other-pkg')
+ }
+ //! Duplicate third-party comment
+ //! Duplicate third-party comment
+ bar()
+ `,
+ "/project/entry.css": /* css */ `
+ @import "./a.css";
+ @import "./b.css";
+ @import "./c.css";
+ @import 'some-pkg/css';
+ `,
+ "/project/a.css": /* css */ `
+ a { zoom: 2 } /*! Copyright notice 1 */
+ /*! Duplicate comment */
+ /*! Duplicate comment */
+ `,
+ "/project/b.css": /* css */ `
+ b { zoom: 2 } /*! Copyright notice 1 */
+ /*! Duplicate comment */
+ /*! Duplicate comment */
+ `,
+ "/project/c.css": /* css */ `
+ /*
+ * @license
+ * Copyright notice 2
+ */
+ c {
+ zoom: 2
+ }
+ /* @preserve This is another comment */
+ `,
+ "/project/node_modules/some-pkg/css/index.css": /* css */ `
+ @import "some-other-pkg/css"; /*! (c) Good Software Corp */
+ /*! Duplicate third-party comment */
+ /*! Duplicate third-party comment */
+ `,
+ "/project/node_modules/some-other-pkg/css/index.css": /* css */ `
+ /*! Duplicate third-party comment */
+ /*! Duplicate third-party comment */
+ .some-other-pkg {
+ zoom: 2
+ }
+ /** @preserve
+ * (c) Evil Software Corp
+ */
+ `,
+ },
+ outdir: "/out",
+ entryPoints: ["/project/entry.js", "/project/entry.css"],
+ minifyWhitespace: true,
+ legalComments: "eof",
+ onAfterBundle(api) {
+ assert(
+ api
+ .readFile("/out/entry.js")
+ .trim()
+ .endsWith(
+ dedent`
+ /*
+ * @license
+ * Copyright notice 2
+ */
+ /*
+ * @preserve
+ * (c) Evil Software Corp
+ */
+ // @preserve This is another comment
+ //! (c) Good Software Corp
+ //! Copyright notice 1
+ //! Duplicate comment
+ //! Duplicate third-party comment
+ `,
+ ),
+ "js should have all copyright notices in order",
+ );
+ assert(
+ api
+ .readFile("/out/entry.css")
+ .trim()
+ .endsWith(
+ dedent`
+ /*
+ * @license
+ * Copyright notice 2
+ */
+ /* @preserve This is another comment */
+ /*! (c) Good Software Corp */
+ /*! Copyright notice 1 */
+ /*! Duplicate comment */
+ /*! Duplicate third-party comment */
+ /** @preserve
+ * (c) Evil Software Corp
+ */
+ `,
+ ),
+ "css should have all copyright notices in order",
+ );
+ },
+ });
+ itBundled("default/LegalCommentsEscapeSlashScriptAndStyleEndOfFile", {
+ files: {
+ "/project/entry.js": `import "js-pkg"; a /*! </script> */`,
+ "/project/node_modules/js-pkg/index.js": `x /*! </script> */`,
+ "/project/entry.css": `@import "css-pkg"; a { b: c } /*! </style> */`,
+ "/project/node_modules/css-pkg/index.css": `x { y: z } /*! </style> */`,
+ },
+ outdir: "/out",
+ entryPoints: ["/project/entry.js", "/project/entry.css"],
+ minifyWhitespace: true,
+ legalComments: "eof",
+ onAfterBundle(api) {
+ assert(!api.readFile("/out/entry.js").includes("</script>"), "js should not contain unescaped script tags");
+ assert(!api.readFile("/out/entry.css").includes("</style>"), "css should not contain unescaped style tags");
+ },
+ });
+ itBundled("default/LegalCommentsEscapeSlashScriptAndStyleExternal", {
+ files: {
+ "/project/entry.js": `import "js-pkg"; a /*! </script> */`,
+ "/project/node_modules/js-pkg/index.js": `x /*! </script> */`,
+ "/project/entry.css": `@import "css-pkg"; a { b: c } /*! </style> */`,
+ "/project/node_modules/css-pkg/index.css": `x { y: z } /*! </style> */`,
+ },
+ outdir: "/out",
+ entryPoints: ["/project/entry.js", "/project/entry.css"],
+ minifyWhitespace: true,
+ legalComments: "external",
+ onAfterBundle(api) {
+ assert(
+ api.readFile("/out/entry.js.LEGAL.txt").includes("</script>"),
+ "js.LEGAL.txt should not escaped the script tags",
+ );
+ assert(
+ api.readFile("/out/entry.css.LEGAL.txt").includes("</style>"),
+ "css.LEGAL.txt should not escaped the style tags",
+ );
+ },
+ });
+ itBundled("default/LegalCommentsManyLinked", {
+ files: {
+ "/project/entry.js": /* js */ `
+ import './a'
+ import './b'
+ import './c'
+ import 'some-pkg/js'
+ `,
+ "/project/a.js": `console.log('in a') //! Copyright notice 1`,
+ "/project/b.js": `console.log('in b') //! Copyright notice 1`,
+ "/project/c.js": /* js */ `
+ function foo() {
+ /*
+ * @license
+ * Copyright notice 2
+ */
+ console.log('in c')
+ // @preserve This is another comment
+ }
+ foo()
+ `,
+ "/project/node_modules/some-pkg/js/index.js": `import "some-other-pkg/js" //! (c) Good Software Corp`,
+ "/project/node_modules/some-other-pkg/js/index.js": /* js */ `
+ function bar() {
+ /*
+ * @preserve
+ * (c) Evil Software Corp
+ */
+ console.log('some-other-pkg')
+ }
+ bar()
+ `,
+ "/project/entry.css": /* css */ `
+ @import "./a.css";
+ @import "./b.css";
+ @import "./c.css";
+ @import 'some-pkg/css';
+ `,
+ "/project/a.css": `a { zoom: 2 } /*! Copyright notice 1 */`,
+ "/project/b.css": `b { zoom: 2 } /*! Copyright notice 1 */`,
+ "/project/c.css": /* css */ `
+ /*
+ * @license
+ * Copyright notice 2
+ */
+ c {
+ zoom: 2
+ }
+ /* @preserve This is another comment */
+ `,
+ "/project/node_modules/some-pkg/css/index.css": `@import "some-other-pkg/css"; /*! (c) Good Software Corp */`,
+ "/project/node_modules/some-other-pkg/css/index.css": /* css */ `
+ .some-other-pkg {
+ zoom: 2
+ }
+ /** @preserve
+ * (c) Evil Software Corp
+ */
+ `,
+ },
+ outdir: "/out",
+ entryPoints: ["/project/entry.js", "/project/entry.css"],
+ minifyWhitespace: true,
+ legalComments: "linked",
+ onAfterBundle(api) {
+ assert(
+ api.readFile("/out/entry.js").endsWith("/*! For license information please see entry.js.LEGAL.txt */\n"),
+ "js should have a legal comment at the end",
+ );
+ assert(
+ api.readFile("/out/entry.css").endsWith("/*! For license information please see entry.css.LEGAL.txt */\n"),
+ "css should have a legal comment at the end",
+ );
+ assert(
+ api.readFile("/out/entry.js.LEGAL.txt").trim(),
+ dedent`
+ /*
+ * @license
+ * Copyright notice 2
+ */
+ /*
+ * @preserve
+ * (c) Evil Software Corp
+ */
+ // @preserve This is another comment
+ //! (c) Good Software Corp
+ //! Copyright notice 1
+ `,
+ );
+ assert.strictEqual(
+ api.readFile("/out/entry.css.LEGAL.txt").trim(),
+ dedent`
+ /*
+ * @license
+ * Copyright notice 2
+ */
+ /* @preserve This is another comment */
+ /*! (c) Good Software Corp */
+ /*! Copyright notice 1 */
+ /** @preserve
+ * (c) Evil Software Corp
+ */
+ `,
+ );
+ },
+ });
+ itBundled("default/IIFE_ES5", {
+ files: {
+ "/entry.js": `console.log('test');`,
+ },
+ unsupportedJSFeatures: ["arrow"],
+ format: "iife",
+ onAfterBundle(api) {
+ assert(api.readFile("/out.js").includes("(function"), "iife should be an es5 function");
+ },
+ });
+ itBundled("default/OutputExtensionRemappingFile", {
+ files: {
+ "/entry.js": `console.log('test');`,
+ },
+ outfile: "/outfile.notjs",
+ onAfterBundle(api) {
+ api.assertFileExists("/outfile.notjs");
+ },
+ });
+ itBundled("default/TopLevelAwaitIIFE", {
+ files: {
+ "/entry.js": /* js */ `
+ await foo;
+ for await (foo of bar) ;
+ `,
+ },
+ format: "iife",
+ bundleErrors: {
+ "/entry.js": ['Top-level await is currently not supported with the "iife" output format'],
+ },
+ });
+ // TODO: doesn't work on esbuild, consider if we want on bun.
+ // itBundled("default/TopLevelAwaitIIFEDeadBranch", {
+ // files: {
+ // "/entry.js": /* js */ `
+ // if (false) await foo;
+ // if (false) for await (foo of bar) ;
+ // `,
+ // },
+ // format: "iife",
+ // });
+ itBundled("default/TopLevelAwaitCJS", {
+ files: {
+ "/entry.js": /* js */ `
+ await foo;
+ for await (foo of bar) ;
+ `,
+ },
+ format: "cjs",
+ bundleErrors: {
+ "/entry.js": ['Top-level await is currently not supported with the "cjs" output format'],
+ },
+ });
+ // TODO: doesn't work on esbuild, consider if we want on bun.
+ // itBundled("default/TopLevelAwaitCJSDeadBranch", {
+ // files: {
+ // "/entry.js": /* js */ `
+ // if (false) await foo;
+ // if (false) for await (foo of bar) ;
+ // `,
+ // },
+ // format: "cjs",
+ // });
+ itBundled("default/TopLevelAwaitESM", {
+ files: {
+ "/entry.js": /* js */ `
+ async function* foo() {
+ yield 1;
+ yield 2;
+ yield 3;
+ return 4;
+ }
+ console.log(await (Promise.resolve(0)));
+ for await (const bar of foo()) console.log(bar);
+ `,
+ },
+ format: "esm",
+ run: {
+ stdout: "0\n1\n2\n3\n",
+ },
+ });
+ itBundled("default/TopLevelAwaitNoBundle", {
+ files: {
+ "/entry.js": /* js */ `
+ await foo;
+ for await (foo of bar) ;
+ `,
+ },
+ mode: "transform",
+ });
+ itBundled("default/TopLevelAwaitForbiddenRequire", {
+ files: {
+ "/entry.js": /* js */ `
+ require('./a')
+ require('./b')
+ require('./c')
+ require('./entry')
+ await 0
+ `,
+ "/a.js": `import './b'`,
+ "/b.js": `import './c'`,
+ "/c.js": `await 0`,
+ },
+ format: "esm",
+ bundleErrors: {
+ "/entry.js": [
+ 'This require call is not allowed because the transitive dependency "c.js" contains a top-level await',
+ 'This require call is not allowed because the transitive dependency "c.js" contains a top-level await',
+ 'This require call is not allowed because the transitive dependency "entry.js" contains a top-level await',
+ ],
+ },
+ });
+ itBundled("default/TopLevelAwaitAllowedImportWithoutSplitting", {
+ files: {
+ "/entry.js": /* js */ `
+ import('./a')
+ import('./b')
+ import('./c')
+ import('./entry')
+ console.log(await 1)
+ `,
+ "/a.js": `import './b'`,
+ "/b.js": `import './c'`,
+ "/c.js": `console.log(await 0)`,
+ },
+ format: "esm",
+ run: {
+ stdout: "0\n1",
+ },
+ });
+ itBundled("default/TopLevelAwaitAllowedImportWithSplitting", {
+ files: {
+ "/entry.js": /* js */ `
+ import('./a')
+ import('./b')
+ import('./c')
+ // Commented out because esbuild doesn't handle this https://github.com/evanw/esbuild/issues/3043
+ // import('./entry')
+ console.log(await 1)
+ `,
+ "/a.js": `import './b'`,
+ "/b.js": `import './c'`,
+ "/c.js": `console.log(await 0)`,
+ },
+ format: "esm",
+ splitting: true,
+ outdir: "/out",
+ run: {
+ file: "/out/entry.js",
+ stdout: "1\n0",
+ },
+ });
+ itBundled("default/AssignToImport", {
+ files: {
+ "/entry.js": /* js */ `
+ import "./bad0.js"
+ import "./bad1.js"
+ import "./bad2.js"
+ import "./bad3.js"
+ import "./bad4.js"
+ import "./bad5.js"
+ import "./bad6.js"
+ import "./bad7.js"
+ import "./bad8.js"
+ import "./bad9.js"
+ import "./bad10.js"
+ import "./bad11.js"
+ import "./bad12.js"
+ import "./bad13.js"
+ import "./bad14.js"
+ import "./bad15.js"
+
+ import "./good0.js"
+ import "./good1.js"
+ import "./good2.js"
+ import "./good3.js"
+ import "./good4.js"
+ `,
+ "/node_modules/foo/index.js": ``,
+ "/bad0.js": `import x from "foo"; x = 1`,
+ "/bad1.js": `import x from "foo"; x++`,
+ "/bad2.js": `import x from "foo"; ([x] = 1)`,
+ "/bad3.js": `import x from "foo"; ({x} = 1)`,
+ "/bad4.js": `import x from "foo"; ({y: x} = 1)`,
+ "/bad5.js": `import {x} from "foo"; x++`,
+ "/bad6.js": `import * as x from "foo"; x++`,
+ "/bad7.js": `import * as x from "foo"; x.y = 1`,
+ "/bad8.js": `import * as x from "foo"; x[y] = 1`,
+ "/bad9.js": `import * as x from "foo"; x['y'] = 1`,
+ "/bad10.js": `import * as x from "foo"; x['y z'] = 1`,
+ "/bad11.js": `import x from "foo"; delete x`,
+ "/bad12.js": `import {x} from "foo"; delete x`,
+ "/bad13.js": `import * as x from "foo"; delete x.y`,
+ "/bad14.js": `import * as x from "foo"; delete x['y']`,
+ "/bad15.js": `import * as x from "foo"; delete x[y]`,
+ "/good0.js": `import x from "foo"; ({y = x} = 1)`,
+ "/good1.js": `import x from "foo"; ({[x]: y} = 1)`,
+ "/good2.js": `import x from "foo"; x.y = 1`,
+ "/good3.js": `import x from "foo"; x[y] = 1`,
+ "/good4.js": `import x from "foo"; x['y'] = 1`,
+ "/good5.js": `import x from "foo"; x['y z'] = 1`,
+ },
+ bundleErrors: {
+ // TODO: get exact errors here. when you do this make sure all bad* files are covered
+ "/bad0.js": ["imports are immutable"],
+ "/bad1.js": ["imports are immutable"],
+ "/bad2.js": ["imports are immutable"],
+ "/bad3.js": ["imports are immutable"],
+ "/bad4.js": ["imports are immutable"],
+ "/bad5.js": ["imports are immutable"],
+ "/bad6.js": ["imports are immutable"],
+ "/bad7.js": ["imports are immutable"],
+ "/bad8.js": ["imports are immutable"],
+ "/bad9.js": ["imports are immutable"],
+ "/bad10.js": ["imports are immutable"],
+ "/bad11.js": ["imports are immutable"],
+ "/bad12.js": ["imports are immutable"],
+ "/bad13.js": ["imports are immutable"],
+ "/bad14.js": ["imports are immutable"],
+ "/bad15.js": ["imports are immutable"],
+ },
+ });
+ itBundled("default/AssignToImportNoBundle", {
+ files: {
+ "/bad0.js": `import x from "foo"; x = 1`,
+ "/bad1.js": `import x from "foo"; x++`,
+ "/bad2.js": `import x from "foo"; ([x] = 1)`,
+ "/bad3.js": `import x from "foo"; ({x} = 1)`,
+ "/bad4.js": `import x from "foo"; ({y: x} = 1)`,
+ "/bad5.js": `import {x} from "foo"; x++`,
+ "/bad6.js": `import * as x from "foo"; x++`,
+ "/uncaught7.js": `import * as x from "foo"; x.y = 1`,
+ "/uncaught8.js": `import * as x from "foo"; x[y] = 1`,
+ "/uncaught9.js": `import * as x from "foo"; x['y'] = 1`,
+ "/uncaught10.js": `import * as x from "foo"; x['y z'] = 1`,
+ "/bad11.js": `import x from "foo"; delete x`,
+ "/bad12.js": `import {x} from "foo"; delete x`,
+ "/uncaught13.js": `import * as x from "foo"; delete x.y`,
+ "/uncaught14.js": `import * as x from "foo"; delete x['y']`,
+ "/uncaught15.js": `import * as x from "foo"; delete x[y]`,
+ "/good0.js": `import x from "foo"; ({y = x} = 1)`,
+ "/good1.js": `import x from "foo"; ({[x]: y} = 1)`,
+ "/good2.js": `import x from "foo"; x.y = 1`,
+ "/good3.js": `import x from "foo"; x[y] = 1`,
+ "/good4.js": `import x from "foo"; x['y'] = 1`,
+ "/good5.js": `import x from "foo"; x['y z'] = 1`,
+ },
+ entryPoints: [
+ "/bad0.js",
+ "/bad1.js",
+ "/bad2.js",
+ "/bad3.js",
+ "/bad4.js",
+ "/bad5.js",
+ "/bad6.js",
+ "/uncaught7.js",
+ "/uncaught8.js",
+ "/uncaught9.js",
+ "/uncaught10.js",
+ "/bad11.js",
+ "/bad12.js",
+ "/uncaught13.js",
+ "/uncaught14.js",
+ "/uncaught15.js",
+ "/good0.js",
+ "/good1.js",
+ "/good2.js",
+ "/good3.js",
+ "/good4.js",
+ "/good5.js",
+ ],
+ bundleErrors: {
+ // TODO: get exact errors here. when you do this make sure all bad* files are covered
+ "/bad0.js": ["imports are immutable"],
+ "/bad1.js": ["imports are immutable"],
+ "/bad2.js": ["imports are immutable"],
+ "/bad3.js": ["imports are immutable"],
+ "/bad4.js": ["imports are immutable"],
+ "/bad5.js": ["imports are immutable"],
+ "/bad6.js": ["imports are immutable"],
+ "/bad11.js": ["imports are immutable"],
+ "/bad12.js": ["imports are immutable"],
+ },
+ });
+ itBundled("default/MinifyArguments", {
+ files: {
+ "/entry.js": /* js */ `
+ function a(x = arguments) {
+ let arguments
+ }
+ function b(x = arguments) {
+ let arguments
+ }
+ function c(x = arguments) {
+ let arguments
+ }
+ a()
+ b()
+ c()
+ `,
+ },
+ minifyIdentifiers: true,
+ format: "iife",
+ onAfterBundle(api) {
+ assert(!api.readFile("/out.js").includes("let arguments"), "let arguments should've been minified");
+ assert(!api.readFile("/out.js").includes("var arguments"), "let arguments should've been minified");
+ assert(api.readFile("/out.js").includes("arguments"), "x = arguments should not have been minified");
+ },
+ });
+ // TODO: this test is very subjective considering bun's warnings may not match esbuild.
+ // This test checks for various cases where code throws warnings, and makes sure that the warnings
+ // are not present when they appear in `node_modules`
+ const WarningsInsideNodeModules = {
+ "/dup-case.js": `switch (x) { case 0: case 0: }`,
+ "/not-in.js": `!a in b`,
+ "/not-instanceof.js": `!a instanceof b`,
+ "/return-asi.js": `return\n123`,
+ "/bad-typeof.js": `typeof x == 'null'`,
+ "/equals-neg-zero.js": `x === -0`,
+ "/equals-nan.js": `x === NaN`,
+ "/equals-object.js": `x === []`,
+ "/write-getter.js": `class Foo { get #foo() {} foo() { this.#foo = 123 } }`,
+ "/read-setter.js": `class Foo { set #foo(x) {} foo() { return this.#foo } }`,
+ "/delete-super.js": `class Foo extends Bar { foo() { delete super.foo } }`,
+ };
+ itBundled("default/WarningsInsideNodeModules", {
+ files: {
+ "/entry.js": Object.keys(WarningsInsideNodeModules)
+ .map(file => `import "./${file}"; import "./node_modules/${file}"; import "@plugin/${file}"`)
+ .join("\n"),
+ ...Object.fromEntries(
+ Object.entries(WarningsInsideNodeModules).flatMap(([file, code]) => [
+ [file, code],
+ [`/node_modules${file}`, code],
+ [`/node_modules/@plugin${file}`, code],
+ ]),
+ ),
+ },
+ bundleWarnings: {
+ "/write-getter.js": [`Writing to getter-only property "#foo" will throw`],
+ "/read-setter.js": [`Reading from setter-only property "#foo" will throw`],
+ },
+ // TODO: could use onAfterBundle to check the above warning object covers all files.
+ });
+ itBundled("default/RequireResolve", {
+ files: {
+ "/entry.js": /* js */ `
+ console.log(require.resolve)
+ console.log(require.resolve())
+ console.log(require.resolve(foo))
+ console.log(require.resolve('a', 'b'))
+ console.log(require.resolve('./present-file'))
+ console.log(require.resolve('./missing-file'))
+ console.log(require.resolve('./external-file'))
+ console.log(require.resolve('missing-pkg'))
+ console.log(require.resolve('external-pkg'))
+ console.log(require.resolve('@scope/missing-pkg'))
+ console.log(require.resolve('@scope/external-pkg'))
+ try {
+ console.log(require.resolve('inside-try'))
+ } catch (e) {
+ }
+ if (false) {
+ console.log(require.resolve('dead-code'))
+ }
+ console.log(false ? require.resolve('dead-if') : 0)
+ console.log(true ? 0 : require.resolve('dead-if'))
+ console.log(false && require.resolve('dead-and'))
+ console.log(true || require.resolve('dead-or'))
+ console.log(true ?? require.resolve('dead-nullish'))
+ `,
+ "/present-file.js": ``,
+ },
+ platform: "node",
+ format: "cjs",
+ external: ["external-pkg", "@scope/external-pkg", "{{root}}/external-file"],
+ });
+ itBundled("default/InjectMissing", {
+ files: {
+ "/entry.js": ``,
+ },
+ inject: ["/inject.js"],
+ bundleErrors: {
+ "/entry.js": ['Could not resolve "/inject.js"'],
+ },
+ });
+ itBundled("default/InjectDuplicate", {
+ files: {
+ "/entry.js": ``,
+ "/inject.js": `console.log('injected')`,
+ },
+ inject: ["/inject.js", "/inject.js"],
+ bundleErrors: {
+ "/entry.js": ['Duplicate injected file "/inject.js"'],
+ },
+ });
+ return;
+ itBundled("default/Inject", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ let sideEffects = console.log('this should be renamed')
+ let collide = 123
+ console.log(obj.prop)
+ console.log(obj.defined)
+ console.log(injectedAndDefined)
+ console.log(injected.and.defined)
+ console.log(chain.prop.test)
+ console.log(chain2.prop2.test)
+ console.log(collide)
+ console.log(re_export)
+ console.log(re.export)
+ `,
+ "/inject.js": /* js */ `
+ 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 { 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/package.json": /* json */ `
+ {
+ "sideEffects": false
+ }
+ `,
+ "/replacement.js": /* js */ `
+ export let replace = {
+ test() {}
+ }
+ let replace2 = {
+ test() {}
+ }
+ export { replace2 as 'chain2.prop2' }
+ `,
+ "/collision.js": `export let collide = 123`,
+ "/re-export.js": /* js */ `
+ export {re_export} from 'external-pkg'
+ export {'re.export'} from 'external-pkg2'
+ `,
+ },
+ format: "cjs",
+ 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": JSON.stringify("defined"),
+ injectedAndDefined: JSON.stringify("should be used"),
+ "injected.and.defined": JSON.stringify("should be used"),
+ },
+ });
+ itBundled("default/InjectNoBundle", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ let sideEffects = console.log('side effects')
+ let collide = 123
+ console.log(obj.prop)
+ console.log(obj.defined)
+ console.log(injectedAndDefined)
+ console.log(injected.and.defined)
+ console.log(chain.prop.test)
+ console.log(chain2.prop2.test)
+ console.log(collide)
+ console.log(re_export)
+ console.log(reexpo.rt)
+ `,
+ "/inject.js": /* js */ `
+ export let obj = {}
+ export let sideEffects = console.log('this should be renamed')
+ export let noSideEffects = /* @__PURE__ */ console.log('side effects')
+ export let injectedAndDefined = 'should not be used'
+ let injected_and_defined = 'should not be used'
+ 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/package.json": /* json */ `
+ {
+ "sideEffects": false
+ }
+ `,
+ "/replacement.js": /* js */ `
+ export let replace = {
+ test() {}
+ }
+ let replaceDot = {
+ test() {}
+ }
+ export { replaceDot as 'chain2.prop2' }
+ `,
+ "/collision.js": `export let collide = 123`,
+ "/re-export.js": /* js */ `
+ export {re_export} from 'external-pkg'
+ export {'reexpo.rt'} from 'external-pkg2'
+ `,
+ },
+ treeShaking: true,
+ mode: "passthrough",
+ 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' }
+ `,
+ },
+ 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"],
+ });
+ itBundled("default/AvoidTDZ", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Foo {
+ static foo = new Foo
+ }
+ let foo = Foo.foo
+ console.log(foo)
+ export class Bar {}
+ export let bar = 123
+ `,
+ },
+ });
+ itBundled("default/AvoidTDZNoBundle", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Foo {
+ static foo = new Foo
+ }
+ let foo = Foo.foo
+ console.log(foo)
+ export class Bar {}
+ export let bar = 123
+ `,
+ },
+ mode: "passthrough",
+ });
+ itBundled("default/DefineImportMeta", {
+ 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.length,
+
+ // This should not be substituted
+ import.meta.main,
+ )
+ `,
+ },
+ define: {
+ "import.meta": 1,
+ "import.meta.foo": "bun!",
+ "import.meta.foo.bar": 3,
+ },
+ run: {
+ stdout: "1 bun! 3 4 undefined",
+ },
+ });
+ itBundled("default/DefineImportMetaES5", {
+ // GENERATED
+ files: {
+ "/replaced.js": `console.log(import.meta.x)`,
+ "/kept.js": `console.log(import.meta.y)`,
+ "/dead-code.js": `var x = () => console.log(import.meta.z)`,
+ },
+ entryPoints: ["/replaced.js", "/kept.js", "/dead-code.js"],
+ 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',
+ }
+ `,
+ },
+ });
+ itBundled("default/DefineThis", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ ok(
+ // These should be fully substituted
+ this,
+ this.foo,
+ this.foo.bar,
+
+ // Should just substitute "this.foo"
+ this.foo.baz,
+
+ // This should not be substituted
+ this.bar,
+ );
+
+ // This code should be the same as above
+ (() => {
+ ok(
+ this,
+ this.foo,
+ this.foo.bar,
+ this.foo.baz,
+ this.bar,
+ );
+ })();
+
+ // Nothing should be substituted in this code
+ (function() {
+ doNotSubstitute(
+ this,
+ this.foo,
+ this.foo.bar,
+ this.foo.baz,
+ this.bar,
+ );
+ })();
+ `,
+ },
+ define: {
+ this: 1,
+ "this.foo": 2,
+ "this.foo.bar": 3,
+ },
+ });
+ itBundled("default/DefineOptionalChain", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ console.log([
+ a.b.c,
+ a?.b.c,
+ a.b?.c,
+ ], [
+ a['b']['c'],
+ a?.['b']['c'],
+ a['b']?.['c'],
+ ], [
+ a[b][c],
+ a?.[b][c],
+ a[b]?.[c],
+ ])
+ `,
+ },
+ define: {
+ "a.b.c": 1,
+ },
+ });
+ itBundled("default/DefineOptionalChainLowered", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ console.log([
+ a.b.c,
+ a?.b.c,
+ a.b?.c,
+ ], [
+ a['b']['c'],
+ a?.['b']['c'],
+ a['b']?.['c'],
+ ], [
+ a[b][c],
+ a?.[b][c],
+ a[b]?.[c],
+ ])
+ `,
+ },
+ define: {
+ "a.b.c": 1,
+ },
+ });
+ itBundled("default/DefineInfiniteLoopIssue2407", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ a.b()
+ x.y()
+ `,
+ },
+ define: {
+ "a.b": "b.c",
+ "b.c": "c.a",
+ "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],
+ )
+ `,
+ },
+ 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()
+ `,
+ },
+ keepNames: true,
+ });
+ 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 }
+
+ 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 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 }
+ `,
+ },
+ mode: "passthrough",
+ });
+ itBundled("default/CharFreqIgnoreComments", {
+ // GENERATED
+ files: {
+ "/a.js": /* js */ `
+ export default function(one, two, three, four) {
+ return 'the argument names must be the same'
+ }
+ `,
+ "/b.js": /* js */ `
+ export default function(one, two, three, four) {
+ return 'the argument names must be the same'
+ }
+
+ // Some comment text to change the character frequency histogram:
+ // ________________________________________________________________________________
+ // FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+ // AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ // IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
+ // LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
+ `,
+ },
+ entryPoints: ["/a.js", "/b.js"],
+ });
+ 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.
+ `, */
+ });
+ 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:
+ `, */
+ });
+ itBundled("default/ConstWithLet", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ const a = 1; console.log(a)
+ if (true) { const b = 2; console.log(b) }
+ if (true) { const b = 3; unknownFn(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/ConstWithLetNoBundle", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ const a = 1; console.log(a)
+ if (true) { const b = 2; console.log(b) }
+ if (true) { const b = 3; unknownFn(b) }
+ for (const c = x;;) console.log(c)
+ for (const d in x) console.log(d)
+ 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`,
+ },
+ platform: "node",
+ });
+ itBundled("default/ExternalES6ConvertedToCommonJS", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ require('./a')
+ require('./b')
+ require('./c')
+ require('./d')
+ require('./e')
+ `,
+ "/a.js": /* js */ `
+ import * as ns from 'x'
+ export {ns}
+ `,
+ "/b.js": /* js */ `
+ import * as ns from 'x' // "ns" must be renamed to avoid collisions with "a.js"
+ export {ns}
+ `,
+ "/c.js": `export * as ns from 'x'`,
+ "/d.js": `export {ns} from 'x'`,
+ "/e.js": `export * from 'x'`,
+ },
+ format: "esm",
+ });
+ 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
+ `, */
+ });
+ itBundled("default/JSXThisValueCommonJS", {
+ // GENERATED
+ files: {
+ "/factory.jsx": /* jsx */ `
+ console.log([
+ <x />,
+ /* @__PURE__ */ this('x', null),
+ ])
+ f = function() {
+ console.log([
+ <y />,
+ /* @__PURE__ */ this('y', null),
+ ])
+ }
+ `,
+ "/fragment.jsx": /* jsx */ `
+ console.log([
+ <>x</>,
+ /* @__PURE__ */ this(this, null, 'x'),
+ ]),
+ f = function() {
+ console.log([
+ <>y</>,
+ /* @__PURE__ */ this(this, null, 'y'),
+ ])
+ }
+ `,
+ },
+ entryPoints: ["/factory.jsx", "/fragment.jsx"],
+ jsx: {
+ factory: "this",
+ fragment: "this",
+ },
+ });
+ itBundled("default/JSXThisValueESM", {
+ // GENERATED
+ files: {
+ "/factory.jsx": /* jsx */ `
+ console.log([
+ <x />,
+ /* @__PURE__ */ this('x', null),
+ ])
+ f = function() {
+ console.log([
+ <y />,
+ /* @__PURE__ */ this('y', null),
+ ])
+ }
+ export {}
+ `,
+ "/fragment.jsx": /* jsx */ `
+ console.log([
+ <>x</>,
+ /* @__PURE__ */ this(this, null, 'x'),
+ ]),
+ f = function() {
+ console.log([
+ <>y</>,
+ /* @__PURE__ */ this(this, null, 'y'),
+ ])
+ }
+ export {}
+ `,
+ },
+ entryPoints: ["/factory.jsx", "/fragment.jsx"],
+ jsx: {
+ factory: "this",
+ fragment: "this",
+ },
+ /* TODO FIX expectedScanLog: `factory.jsx: DEBUG: Top-level "this" will be replaced with undefined since this file is an ECMAScript module
+ factory.jsx: NOTE: This file is considered to be an ECMAScript module because of the "export" keyword here:
+ fragment.jsx: DEBUG: Top-level "this" will be replaced with undefined since this file is an ECMAScript module
+ fragment.jsx: NOTE: This file is considered to be an ECMAScript module because of the "export" keyword here:
+ `, */
+ });
+ itBundled("default/JSXThisPropertyCommonJS", {
+ // GENERATED
+ files: {
+ "/factory.jsx": /* jsx */ `
+ console.log([
+ <x />,
+ /* @__PURE__ */ this.factory('x', null),
+ ])
+ f = function() {
+ console.log([
+ <y />,
+ /* @__PURE__ */ this.factory('y', null),
+ ])
+ }
+ `,
+ "/fragment.jsx": /* jsx */ `
+ console.log([
+ <>x</>,
+ /* @__PURE__ */ this.factory(this.fragment, null, 'x'),
+ ]),
+ f = function() {
+ console.log([
+ <>y</>,
+ /* @__PURE__ */ this.factory(this.fragment, null, 'y'),
+ ])
+ }
+ `,
+ },
+ entryPoints: ["/factory.jsx", "/fragment.jsx"],
+ jsx: {
+ factory: "this.factory",
+ fragment: "this.fragment",
+ },
+ });
+ itBundled("default/JSXThisPropertyESM", {
+ // GENERATED
+ files: {
+ "/factory.jsx": /* jsx */ `
+ console.log([
+ <x />,
+ /* @__PURE__ */ this.factory('x', null),
+ ])
+ f = function() {
+ console.log([
+ <y />,
+ /* @__PURE__ */ this.factory('y', null),
+ ])
+ }
+ export {}
+ `,
+ "/fragment.jsx": /* jsx */ `
+ console.log([
+ <>x</>,
+ /* @__PURE__ */ this.factory(this.fragment, null, 'x'),
+ ]),
+ f = function() {
+ console.log([
+ <>y</>,
+ /* @__PURE__ */ this.factory(this.fragment, null, 'y'),
+ ])
+ }
+ export {}
+ `,
+ },
+ entryPoints: ["/factory.jsx", "/fragment.jsx"],
+ jsx: {
+ factory: "this.factory",
+ fragment: "this.fragment",
+ },
+ /* TODO FIX expectedScanLog: `factory.jsx: DEBUG: Top-level "this" will be replaced with undefined since this file is an ECMAScript module
+ factory.jsx: NOTE: This file is considered to be an ECMAScript module because of the "export" keyword here:
+ fragment.jsx: DEBUG: Top-level "this" will be replaced with undefined since this file is an ECMAScript module
+ fragment.jsx: NOTE: This file is considered to be an ECMAScript module because of the "export" keyword here:
+ `, */
+ });
+ itBundled("default/JSXImportMetaValue", {
+ // GENERATED
+ files: {
+ "/factory.jsx": /* jsx */ `
+ console.log([
+ <x />,
+ /* @__PURE__ */ import.meta('x', null),
+ ])
+ f = function() {
+ console.log([
+ <y />,
+ /* @__PURE__ */ import.meta('y', null),
+ ])
+ }
+ export {}
+ `,
+ "/fragment.jsx": /* jsx */ `
+ console.log([
+ <>x</>,
+ /* @__PURE__ */ import.meta(import.meta, null, 'x'),
+ ]),
+ f = function() {
+ console.log([
+ <>y</>,
+ /* @__PURE__ */ import.meta(import.meta, null, 'y'),
+ ])
+ }
+ export {}
+ `,
+ },
+ entryPoints: ["/factory.jsx", "/fragment.jsx"],
+ unsupportedJSFeatures: "ImportMeta",
+ jsx: {
+ factory: "import.meta",
+ fragment: "import.meta",
+ },
+ /* TODO FIX expectedScanLog: `factory.jsx: WARNING: "import.meta" is not available in the configured target environment and will be empty
+ factory.jsx: WARNING: "import.meta" is not available in the configured target environment and will be empty
+ fragment.jsx: WARNING: "import.meta" is not available in the configured target environment and will be empty
+ fragment.jsx: WARNING: "import.meta" is not available in the configured target environment and will be empty
+ fragment.jsx: WARNING: "import.meta" is not available in the configured target environment and will be empty
+ fragment.jsx: WARNING: "import.meta" is not available in the configured target environment and will be empty
+ `, */
+ });
+ itBundled("default/JSXImportMetaProperty", {
+ // GENERATED
+ files: {
+ "/factory.jsx": /* jsx */ `
+ console.log([
+ <x />,
+ /* @__PURE__ */ import.meta.factory('x', null),
+ ])
+ f = function() {
+ console.log([
+ <y />,
+ /* @__PURE__ */ import.meta.factory('y', null),
+ ])
+ }
+ export {}
+ `,
+ "/fragment.jsx": /* jsx */ `
+ console.log([
+ <>x</>,
+ /* @__PURE__ */ import.meta.factory(import.meta.fragment, null, 'x'),
+ ]),
+ f = function() {
+ console.log([
+ <>y</>,
+ /* @__PURE__ */ import.meta.factory(import.meta.fragment, null, 'y'),
+ ])
+ }
+ export {}
+ `,
+ },
+ entryPoints: ["/factory.jsx", "/fragment.jsx"],
+ unsupportedJSFeatures: "ImportMeta",
+ jsx: {
+ factory: "import.meta.factory",
+ fragment: "import.meta.fragment",
+ },
+ /* TODO FIX expectedScanLog: `factory.jsx: WARNING: "import.meta" is not available in the configured target environment and will be empty
+ factory.jsx: WARNING: "import.meta" is not available in the configured target environment and will be empty
+ fragment.jsx: WARNING: "import.meta" is not available in the configured target environment and will be empty
+ fragment.jsx: WARNING: "import.meta" is not available in the configured target environment and will be empty
+ fragment.jsx: WARNING: "import.meta" is not available in the configured target environment and will be empty
+ fragment.jsx: WARNING: "import.meta" is not available in the configured target environment and will be empty
+ `, */
+ });
+ itBundled("default/BundlingFilesOutsideOfOutbase", {
+ // GENERATED
+ files: {
+ "/src/entry.js": `console.log('test')`,
+ },
+ splitting: true,
+ format: "esm",
+ outbase: "/some/nested/directory",
+ });
+ const relocateFiles = {
+ "/top-level.js": /* js */ `
+ var a;
+ for (var b; 0;);
+ for (var { c, x: [d] } = {}; 0;);
+ for (var e of []);
+ for (var { f, x: [g] } of []);
+ for (var h in {});
+ for (var i = 1 in {});
+ for (var { j, x: [k] } in {});
+ function l() {}
+ `,
+ "/nested.js": /* js */ `
+ if (true) {
+ var a;
+ for (var b; 0;);
+ for (var { c, x: [d] } = {}; 0;);
+ for (var e of []);
+ for (var { f, x: [g] } of []);
+ for (var h in {});
+ for (var i = 1 in {});
+ for (var { j, x: [k] } in {});
+ function l() {}
+ }
+ `,
+ "/let.js": /* js */ `
+ if (true) {
+ let a;
+ for (let b; 0;);
+ for (let { c, x: [d] } = {}; 0;);
+ for (let e of []);
+ for (let { f, x: [g] } of []);
+ for (let h in {});
+ // for (let i = 1 in {});
+ for (let { j, x: [k] } in {});
+ }
+ `,
+ "/function.js": /* js */ `
+ function x() {
+ var a;
+ for (var b; 0;);
+ for (var { c, x: [d] } = {}; 0;);
+ for (var e of []);
+ for (var { f, x: [g] } of []);
+ for (var h in {});
+ for (var i = 1 in {});
+ for (var { j, x: [k] } in {});
+ function l() {}
+ }
+ x()
+ `,
+ "/function-nested.js": /* js */ `
+ function x() {
+ if (true) {
+ var a;
+ for (var b; 0;);
+ for (var { c, x: [d] } = {}; 0;);
+ for (var e of []);
+ for (var { f, x: [g] } of []);
+ for (var h in {});
+ for (var i = 1 in {});
+ for (var { j, x: [k] } in {});
+ function l() {}
+ }
+ }
+ x()
+ `,
+ };
+ const relocateEntries = ["/top-level.js", "/nested.js", "/let.js", "/function.js", "/function-nested.js"];
+
+ itBundled("default/VarRelocatingBundle", {
+ // GENERATED
+ files: relocateFiles,
+ entryPoints: relocateEntries,
+ format: "esm",
+ });
+ itBundled("default/VarRelocatingNoBundle", {
+ // GENERATED
+ files: relocateFiles,
+ entryPoints: relocateEntries,
+ format: "esm",
+ mode: "convertformat",
+ });
+ itBundled("default/ImportNamespaceThisValue", {
+ // GENERATED
+ files: {
+ "/a.js": /* js */ `
+ import def, * as ns from 'external'
+ console.log(ns[foo](), new ns[foo]())
+ `,
+ "/b.js": /* js */ `
+ import def, * as ns from 'external'
+ console.log(ns.foo(), new ns.foo())
+ `,
+ "/c.js": /* js */ `
+ import def, {foo} from 'external'
+ console.log(def(), foo())
+ console.log(new def(), new foo())
+ `,
+ },
+ entryPoints: ["/a.js", "/b.js", "/c.js"],
+ format: "cjs",
+ });
+ itBundled("default/ThisUndefinedWarningESM", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import x from './file1.js'
+ import y from 'pkg/file2.js'
+ console.log(x, y)
+ `,
+ "/file1.js": `export default [this, this]`,
+ "/node_modules/pkg/file2.js": `export default [this, this]`,
+ },
+ /* TODO FIX expectedScanLog: `file1.js: DEBUG: Top-level "this" will be replaced with undefined since this file is an ECMAScript module
+ file1.js: NOTE: This file is considered to be an ECMAScript module because of the "export" keyword here:
+ node_modules/pkg/file2.js: DEBUG: Top-level "this" will be replaced with undefined since this file is an ECMAScript module
+ node_modules/pkg/file2.js: NOTE: This file is considered to be an ECMAScript module because of the "export" keyword here:
+ `, */
+ });
+ itBundled("default/QuotedProperty", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from 'ext'
+ console.log(ns.mustBeUnquoted, ns['mustBeQuoted'])
+ `,
+ },
+ format: "cjs",
+ });
+ itBundled("default/QuotedPropertyMangle", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from 'ext'
+ console.log(ns.mustBeUnquoted, ns['mustBeUnquoted2'])
+ `,
+ },
+ format: "cjs",
+ minifySyntax: true,
+ });
+ itBundled("default/DuplicatePropertyWarning", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import './outside-node-modules'
+ import 'inside-node-modules'
+ `,
+ "/outside-node-modules/index.jsx": `console.log({ a: 1, a: 2 }, <div a2 a2={3}/>)`,
+ "/outside-node-modules/package.json": `{ "b": 1, "b": 2 }`,
+ "/node_modules/inside-node-modules/index.jsx": `console.log({ c: 1, c: 2 }, <div c2 c2={3}/>)`,
+ "/node_modules/inside-node-modules/package.json": `{ "d": 1, "d": 2 }`,
+ },
+ /* TODO FIX expectedScanLog: `outside-node-modules/index.jsx: WARNING: Duplicate key "a" in object literal
+ outside-node-modules/index.jsx: NOTE: The original key "a" is here:
+ outside-node-modules/index.jsx: WARNING: Duplicate "a2" attribute in JSX element
+ outside-node-modules/index.jsx: NOTE: The original "a2" attribute is here:
+ outside-node-modules/package.json: WARNING: Duplicate key "b" in object literal
+ outside-node-modules/package.json: NOTE: The original key "b" is here:
+ `, */
+ });
+ itBundled("default/RequireShimSubstitution", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ console.log([
+ require,
+ typeof require,
+ require('./example.json'),
+ require('./example.json', { type: 'json' }),
+ require(window.SOME_PATH),
+ module.require('./example.json'),
+ module.require('./example.json', { type: 'json' }),
+ module.require(window.SOME_PATH),
+ require.resolve('some-path'),
+ require.resolve(window.SOME_PATH),
+ import('some-path'),
+ import(window.SOME_PATH),
+ ])
+ `,
+ "/example.json": `{ "works": true }`,
+ },
+ external: ["some-path"],
+ });
+ itBundled("default/StrictModeNestedFnDeclKeepNamesVariableInliningIssue1552", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ export function outer() {
+ {
+ function inner() {
+ return Math.random();
+ }
+ const x = inner();
+ console.log(x);
+ }
+ }
+ outer();
+ `,
+ },
+ keepNames: true,
+ mode: "passthrough",
+ });
+ itBundled("default/BuiltInNodeModulePrecedence", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ console.log([
+ // These are node core modules
+ require('fs'),
+ require('fs/promises'),
+ require('node:foo'),
+
+ // These are not node core modules
+ require('fs/abc'),
+ require('fs/'),
+ ])
+ `,
+ "/node_modules/fs/abc.js": `console.log('include this')`,
+ "/node_modules/fs/index.js": `console.log('include this too')`,
+ "/node_modules/fs/promises.js": `throw 'DO NOT INCLUDE THIS'`,
+ },
+ platform: "node",
+ format: "cjs",
+ });
+ itBundled("default/EntryNamesNoSlashAfterDir", {
+ // GENERATED
+ files: {
+ "/src/app1/main.ts": `console.log(1)`,
+ "/src/app2/main.ts": `console.log(2)`,
+ "/src/app3/main.ts": `console.log(3)`,
+ },
+ entryPointsAdvanced: [
+ { input: "/src/app1/main.ts" },
+ { input: "/src/app2/main.ts" },
+ { input: "/src/app3/main.ts", output: "customPath" },
+ ],
+ entryNames: "[dir]-[name].[ext]",
+ mode: "passthrough",
+ });
+ itBundled("default/EntryNamesNonPortableCharacter", {
+ // GENERATED
+ // TODO: I think this is impossible with the CLI. and also very unsafe with paths.
+ files: {
+ "/entry1-*.ts": `console.log(1)`,
+ "/entry2-*.ts": `console.log(2)`,
+ },
+ entryPointsAdvanced: [
+ // The "*" should turn into "_" for cross-platform Windows portability
+ { input: "/entry1-*.ts" },
+ // The "*" should be preserved since the user _really_ wants it
+ { input: "/entry2-*.ts", output: "entry2-*" },
+ ],
+ mode: "passthrough",
+ });
+ itBundled("default/EntryNamesChunkNamesExtPlaceholder", {
+ // GENERATED
+ files: {
+ "/src/entries/entry1.js": `import "../lib/shared.js"; import "./entry1.css"; console.log('entry1')`,
+ "/src/entries/entry2.js": `import "../lib/shared.js"; import "./entry2.css"; console.log('entry2')`,
+ "/src/entries/entry1.css": `a:after { content: "entry1" }`,
+ "/src/entries/entry2.css": `a:after { content: "entry2" }`,
+ "/src/lib/shared.js": `console.log('shared')`,
+ },
+ entryPoints: ["/src/entries/entry1.js", "/src/entries/entry2.js"],
+ outbase: "/src",
+ splitting: true,
+ entryNames: "main/[ext]/[name]-[hash].[ext]",
+ });
+ itBundled("default/MinifyIdentifiersImportPathFrequencyAnalysis", {
+ // GENERATED
+ files: {
+ "/import.js": /* js */ `
+ import foo from "./WWWWWWWWWWXXXXXXXXXXYYYYYYYYYYZZZZZZZZZZ"
+ console.log(foo, 'no identifier in this file should be named W, X, Y, or Z')
+ `,
+ "/WWWWWWWWWWXXXXXXXXXXYYYYYYYYYYZZZZZZZZZZ.js": `export default 123`,
+ "/require.js": /* js */ `
+ const foo = require("./AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDD")
+ console.log(foo, 'no identifier in this file should be named A, B, C, or D')
+ `,
+ "/AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDD.js": `module.exports = 123`,
+ },
+ entryPoints: ["/import.js", "/require.js"],
+ minifyWhitespace: true,
+ });
+ itBundled("default/ToESMWrapperOmission", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import 'a_nowrap'
+
+ import { b } from 'b_nowrap'
+ b()
+
+ export * from 'c_nowrap'
+
+ import * as d from 'd_WRAP'
+ x = d.x
+
+ import e from 'e_WRAP'
+ e()
+
+ import { default as f } from 'f_WRAP'
+ f()
+
+ import { __esModule as g } from 'g_WRAP'
+ g()
+
+ import * as h from 'h_WRAP'
+ x = h
+
+ import * as i from 'i_WRAP'
+ i.x()
+
+ import * as j from 'j_WRAP'
+ j.x\` + "\`\`" + \`
+
+ x = import("k_WRAP")
+ `,
+ },
+ format: "cjs",
+ mode: "convertformat",
+ });
+ itBundled("default/NamedFunctionExpressionArgumentCollision", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ let x = function foo(foo) {
+ var foo;
+ return foo;
+ }
+ `,
+ },
+ mode: "passthrough",
+ });
+ itBundled("default/NoWarnCommonJSExportsInESMPassThrough", {
+ // GENERATED
+ files: {
+ "/cjs-in-esm.js": /* js */ `
+ export let foo = 1
+ exports.foo = 2
+ module.exports = 3
+ `,
+ "/import-in-cjs.js": /* js */ `
+ import { foo } from 'bar'
+ exports.foo = foo
+ module.exports = foo
+ `,
+ "/no-warnings-here.js": `console.log(module, exports)`,
+ },
+ entryPoints: ["/cjs-in-esm.js", "/import-in-cjs.js", "/no-warnings-here.js"],
+ mode: "passthrough",
+ });
+ itBundled("default/WarnCommonJSExportsInESMConvert", {
+ // GENERATED
+ files: {
+ "/cjs-in-esm.js": /* js */ `
+ export let foo = 1
+ exports.foo = 2
+ module.exports = 3
+ `,
+ "/cjs-in-esm2.js": /* js */ `
+ export let foo = 1
+ module.exports.bar = 3
+ `,
+ "/import-in-cjs.js": /* js */ `
+ import { foo } from 'bar'
+ exports.foo = foo
+ module.exports = foo
+ module.exports.bar = foo
+ `,
+ "/no-warnings-here.js": `console.log(module, exports)`,
+ },
+ entryPoints: ["/cjs-in-esm.js", "/cjs-in-esm2.js", "/import-in-cjs.js", "/no-warnings-here.js"],
+ mode: "convertformat",
+ /* TODO FIX expectedScanLog: `cjs-in-esm.js: WARNING: The CommonJS "exports" variable is treated as a global variable in an ECMAScript module and may not work as expected
+ cjs-in-esm.js: NOTE: This file is considered to be an ECMAScript module because of the "export" keyword here:
+ cjs-in-esm.js: WARNING: The CommonJS "module" variable is treated as a global variable in an ECMAScript module and may not work as expected
+ cjs-in-esm.js: NOTE: This file is considered to be an ECMAScript module because of the "export" keyword here:
+ cjs-in-esm2.js: WARNING: The CommonJS "module" variable is treated as a global variable in an ECMAScript module and may not work as expected
+ cjs-in-esm2.js: NOTE: This file is considered to be an ECMAScript module because of the "export" keyword here:
+ `, */
+ });
+ itBundled("default/WarnCommonJSExportsInESMBundle", {
+ // GENERATED
+ files: {
+ "/cjs-in-esm.js": /* js */ `
+ export let foo = 1
+ exports.foo = 2
+ module.exports = 3
+ `,
+ "/import-in-cjs.js": /* js */ `
+ import { foo } from 'bar'
+ exports.foo = foo
+ module.exports = foo
+ `,
+ "/no-warnings-here.js": `console.log(module, exports)`,
+ },
+ entryPoints: ["/cjs-in-esm.js", "/import-in-cjs.js", "/no-warnings-here.js"],
+ format: "cjs",
+ /* TODO FIX expectedScanLog: `cjs-in-esm.js: WARNING: The CommonJS "exports" variable is treated as a global variable in an ECMAScript module and may not work as expected
+ cjs-in-esm.js: NOTE: This file is considered to be an ECMAScript module because of the "export" keyword here:
+ cjs-in-esm.js: WARNING: The CommonJS "module" variable is treated as a global variable in an ECMAScript module and may not work as expected
+ cjs-in-esm.js: NOTE: This file is considered to be an ECMAScript module because of the "export" keyword here:
+ `, */
+ });
+ itBundled("default/MangleProps", {
+ // GENERATED
+ files: {
+ "/entry1.js": /* js */ `
+ export function shouldMangle() {
+ let foo = {
+ bar_: 0,
+ baz_() {},
+ };
+ let { bar_ } = foo;
+ ({ bar_ } = foo);
+ class foo_ {
+ bar_ = 0
+ baz_() {}
+ static bar_ = 0
+ static baz_() {}
+ }
+ return { bar_, foo_ }
+ }
+
+ export function shouldNotMangle() {
+ let foo = {
+ 'bar_': 0,
+ 'baz_'() {},
+ };
+ let { 'bar_': bar_ } = foo;
+ ({ 'bar_': bar_ } = foo);
+ class foo_ {
+ 'bar_' = 0
+ 'baz_'() {}
+ static 'bar_' = 0
+ static 'baz_'() {}
+ }
+ return { 'bar_': bar_, 'foo_': foo_ }
+ }
+ `,
+ "/entry2.js": /* js */ `
+ export default {
+ bar_: 0,
+ 'baz_': 1,
+ }
+ `,
+ },
+ entryPoints: ["/entry1.js", "/entry2.js"],
+ mode: "passthrough",
+ });
+ itBundled("default/ManglePropsMinify", {
+ // GENERATED
+ files: {
+ "/entry1.js": /* js */ `
+ export function shouldMangle_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX() {
+ let foo = {
+ bar_: 0,
+ baz_() {},
+ };
+ let { bar_ } = foo;
+ ({ bar_ } = foo);
+ class foo_ {
+ bar_ = 0
+ baz_() {}
+ static bar_ = 0
+ static baz_() {}
+ }
+ return { bar_, foo_ }
+ }
+
+ export function shouldNotMangle_YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY() {
+ let foo = {
+ 'bar_': 0,
+ 'baz_'() {},
+ };
+ let { 'bar_': bar_ } = foo;
+ ({ 'bar_': bar_ } = foo);
+ class foo_ {
+ 'bar_' = 0
+ 'baz_'() {}
+ static 'bar_' = 0
+ static 'baz_'() {}
+ }
+ return { 'bar_': bar_, 'foo_': foo_ }
+ }
+ `,
+ "/entry2.js": /* js */ `
+ export default {
+ bar_: 0,
+ 'baz_': 1,
+ }
+ `,
+ },
+ entryPoints: ["/entry1.js", "/entry2.js"],
+ mangleProps: /_$/,
+ minifyIdentifiers: true,
+ mode: "passthrough",
+ });
+ itBundled("default/ManglePropsKeywordPropertyMinify", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Foo {
+ static bar = { get baz() { return 123 } }
+ }
+ `,
+ },
+ mangleProps: /./,
+ minifyIdentifiers: true,
+ minifySyntax: true,
+ mode: "passthrough",
+ });
+ itBundled("default/ManglePropsOptionalChain", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ export default function(x) {
+ x.foo_;
+ x.foo_?.();
+ x?.foo_;
+ x?.foo_();
+ x?.foo_.bar_;
+ x?.foo_.bar_();
+ x?.['foo_'].bar_;
+ x?.foo_['bar_'];
+ }
+ `,
+ },
+ mode: "passthrough",
+ });
+ itBundled("default/ManglePropsLoweredOptionalChain", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ export default function(x) {
+ x.foo_;
+ x.foo_?.();
+ x?.foo_;
+ x?.foo_();
+ x?.foo_.bar_;
+ x?.foo_.bar_();
+ x?.['foo_'].bar_;
+ x?.foo_['bar_'];
+ }
+ `,
+ },
+ mangleProps: /_$/,
+ mode: "passthrough",
+ });
+ itBundled("default/ReserveProps", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ export default {
+ foo_: 0,
+ _bar_: 1,
+ }
+ `,
+ },
+ mangleProps: /_$/,
+ mode: "passthrough",
+ });
+ itBundled("default/ManglePropsImportExport", {
+ // GENERATED
+ files: {
+ "/esm.js": /* js */ `
+ export let foo_ = 123
+ import { bar_ } from 'xyz'
+ `,
+ "/cjs.js": /* js */ `
+ exports.foo_ = 123
+ let bar_ = require('xyz').bar_
+ `,
+ },
+ entryPoints: ["/esm.js", "/cjs.js"],
+ mode: "passthrough",
+ });
+ itBundled("default/ManglePropsImportExportBundled", {
+ // GENERATED
+ files: {
+ "/entry-esm.js": /* js */ `
+ import { esm_foo_ } from './esm'
+ import { cjs_foo_ } from './cjs'
+ import * as esm from './esm'
+ import * as cjs from './cjs'
+ export let bar_ = [
+ esm_foo_,
+ cjs_foo_,
+ esm.esm_foo_,
+ cjs.cjs_foo_,
+ ]
+ `,
+ "/entry-cjs.js": /* js */ `
+ let { esm_foo_ } = require('./esm')
+ let { cjs_foo_ } = require('./cjs')
+ exports.bar_ = [
+ esm_foo_,
+ cjs_foo_,
+ ]
+ `,
+ "/esm.js": `export let esm_foo_ = 'foo'`,
+ "/cjs.js": `exports.cjs_foo_ = 'foo'`,
+ },
+ entryPoints: ["/entry-esm.js", "/entry-cjs.js"],
+ });
+ itBundled("default/ManglePropsJSXTransform", {
+ // GENERATED
+ files: {
+ "/entry.jsx": /* jsx */ `
+ let Foo = {
+ Bar_(props) {
+ return <>{props.text_}</>
+ },
+ hello_: 'hello, world',
+ createElement_(...args) {
+ console.log('createElement', ...args)
+ },
+ Fragment_(...args) {
+ console.log('Fragment', ...args)
+ },
+ }
+ export default <Foo.Bar_ text_={Foo.hello_}></Foo.Bar_>
+ `,
+ },
+ mangleProps: /_$/,
+ mode: "passthrough",
+ });
+ itBundled("default/ManglePropsJSXPreserve", {
+ // GENERATED
+ files: {
+ "/entry.jsx": /* jsx */ `
+ let Foo = {
+ Bar_(props) {
+ return <>{props.text_}</>
+ },
+ hello_: 'hello, world',
+ }
+ export default <Foo.Bar_ text_={Foo.hello_}></Foo.Bar_>
+ `,
+ },
+ outfile: "/out.jsx",
+ mangleProps: /_$/,
+ mode: "passthrough",
+ });
+ itBundled("default/ManglePropsJSXTransformNamespace", {
+ // GENERATED
+ files: {
+ "/entry.jsx": /* jsx */ `
+ export default [
+ <KEEP_THIS_ />,
+ <KEEP:THIS_ />,
+ <foo KEEP:THIS_ />,
+ ]
+ `,
+ },
+ mode: "passthrough",
+ });
+ itBundled("default/ManglePropsAvoidCollisions", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ export default {
+ foo_: 0, // Must not be named "a"
+ bar_: 1, // Must not be named "b"
+ a: 2,
+ b: 3,
+ __proto__: {}, // Always avoid mangling this
+ }
+ `,
+ },
+ mode: "passthrough",
+ });
+ itBundled("default/ManglePropsTypeScriptFeatures", {
+ // GENERATED
+ files: {
+ "/parameter-properties.ts": /* ts */ `
+ class Foo {
+ constructor(
+ public KEEP_FIELD: number,
+ public MANGLE_FIELD_: number,
+ ) {
+ }
+ }
+
+ let foo = new Foo
+ console.log(foo.KEEP_FIELD, foo.MANGLE_FIELD_)
+ `,
+ "/namespace-exports.ts": /* ts */ `
+ namespace ns {
+ export var MANGLE_VAR_ = 1
+ export let MANGLE_LET_ = 2
+ export const MANGLE_CONST_ = 3
+ export let { NESTED_: { DESTRUCTURING_ } } = 4
+ export function MANGLE_FUNCTION_() {}
+ export class MANGLE_CLASS_ {}
+ export namespace MANGLE_NAMESPACE_ { ; }
+ export enum MANGLE_ENUM_ {}
+
+ console.log({
+ VAR: MANGLE_VAR_,
+ LET: MANGLE_LET_,
+ CONST: MANGLE_CONST_,
+ DESTRUCTURING: DESTRUCTURING_,
+ FUNCTION: MANGLE_FUNCTION_,
+ CLASS: MANGLE_CLASS_,
+ NAMESPACE: MANGLE_NAMESPACE_,
+ ENUM: MANGLE_ENUM_,
+ })
+ }
+
+ console.log({
+ VAR: ns.MANGLE_VAR_,
+ LET: ns.MANGLE_LET_,
+ CONST: ns.MANGLE_CONST_,
+ DESTRUCTURING: ns.DESTRUCTURING_,
+ FUNCTION: ns.MANGLE_FUNCTION_,
+ CLASS: ns.MANGLE_CLASS_,
+ NAMESPACE: ns.MANGLE_NAMESPACE_,
+ ENUM: ns.MANGLE_ENUM_,
+ })
+
+ namespace ns {
+ console.log({
+ VAR: MANGLE_VAR_,
+ LET: MANGLE_LET_,
+ CONST: MANGLE_CONST_,
+ DESTRUCTURING: DESTRUCTURING_,
+ FUNCTION: MANGLE_FUNCTION_,
+ CLASS: MANGLE_CLASS_,
+ NAMESPACE: MANGLE_NAMESPACE_,
+ ENUM: MANGLE_ENUM_,
+ })
+ }
+ `,
+ "/enum-values.ts": /* ts */ `
+ enum TopLevelNumber { foo_ = 0 }
+ enum TopLevelString { bar_ = '' }
+ console.log({
+ foo: TopLevelNumber.foo_,
+ bar: TopLevelString.bar_,
+ })
+
+ function fn() {
+ enum NestedNumber { foo_ = 0 }
+ enum NestedString { bar_ = '' }
+ console.log({
+ foo: TopLevelNumber.foo_,
+ bar: TopLevelString.bar_,
+ })
+ }
+ `,
+ },
+ entryPoints: ["/parameter-properties.ts", "/namespace-exports.ts", "/enum-values.ts"],
+ mode: "passthrough",
+ });
+ itBundled("default/ManglePropsShorthand", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ // This should print as "({ y }) => ({ y })" not "({ y: y }) => ({ y: y })"
+ export let yyyyy = ({ xxxxx }) => ({ xxxxx })
+ `,
+ },
+ mangleProps: /x/,
+ mode: "passthrough",
+ });
+ itBundled("default/ManglePropsNoShorthand", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ // This should print as "({ y }) => ({ y: y })" not "({ y: y }) => ({ y: y })"
+ export let yyyyy = ({ xxxxx }) => ({ xxxxx })
+ `,
+ },
+ mangleProps: /x/,
+ minifyIdentifiers: true,
+ mode: "passthrough",
+ });
+ itBundled("default/ManglePropsLoweredClassFields", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Foo {
+ foo_ = 123
+ static bar_ = 234
+ }
+ Foo.bar_ = new Foo().foo_
+ `,
+ },
+ mangleProps: /_$/,
+ mode: "passthrough",
+ });
+ itBundled("default/ManglePropsSuperCall", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Foo {}
+ class Bar extends Foo {
+ constructor() {
+ super();
+ }
+ }
+ `,
+ },
+ mode: "passthrough",
+ });
+ itBundled("default/MangleNoQuotedProps", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ x['_doNotMangleThis'];
+ x?.['_doNotMangleThis'];
+ x[y ? '_doNotMangleThis' : z];
+ x?.[y ? '_doNotMangleThis' : z];
+ x[y ? z : '_doNotMangleThis'];
+ x?.[y ? z : '_doNotMangleThis'];
+ ({ '_doNotMangleThis': x });
+ (class { '_doNotMangleThis' = x });
+ var { '_doNotMangleThis': x } = y;
+ '_doNotMangleThis' in x;
+ (y ? '_doNotMangleThis' : z) in x;
+ (y ? z : '_doNotMangleThis') in x;
+ `,
+ },
+ mangleProps: /_/,
+ mode: "passthrough",
+ });
+ itBundled("default/MangleNoQuotedPropsMinifySyntax", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ x['_doNotMangleThis'];
+ x?.['_doNotMangleThis'];
+ x[y ? '_doNotMangleThis' : z];
+ x?.[y ? '_doNotMangleThis' : z];
+ x[y ? z : '_doNotMangleThis'];
+ x?.[y ? z : '_doNotMangleThis'];
+ ({ '_doNotMangleThis': x });
+ (class { '_doNotMangleThis' = x });
+ var { '_doNotMangleThis': x } = y;
+ '_doNotMangleThis' in x;
+ (y ? '_doNotMangleThis' : z) in x;
+ (y ? z : '_doNotMangleThis') in x;
+ `,
+ },
+ mangleProps: /_/,
+ mangleQuoted: false,
+ mode: "passthrough",
+ });
+ itBundled("default/MangleQuotedProps", {
+ // GENERATED
+ files: {
+ "/keep.js": /* js */ `
+ foo("_keepThisProperty");
+ foo((x, "_keepThisProperty"));
+ foo(x ? "_keepThisProperty" : "_keepThisPropertyToo");
+ x[foo("_keepThisProperty")];
+ x?.[foo("_keepThisProperty")];
+ ({ [foo("_keepThisProperty")]: x });
+ (class { [foo("_keepThisProperty")] = x });
+ var { [foo("_keepThisProperty")]: x } = y;
+ foo("_keepThisProperty") in x;
+ `,
+ "/mangle.js": /* js */ `
+ x['_mangleThis'];
+ x?.['_mangleThis'];
+ x[y ? '_mangleThis' : z];
+ x?.[y ? '_mangleThis' : z];
+ x[y ? z : '_mangleThis'];
+ x?.[y ? z : '_mangleThis'];
+ x[y, '_mangleThis'];
+ x?.[y, '_mangleThis'];
+ ({ '_mangleThis': x });
+ ({ ['_mangleThis']: x });
+ ({ [(y, '_mangleThis')]: x });
+ (class { '_mangleThis' = x });
+ (class { ['_mangleThis'] = x });
+ (class { [(y, '_mangleThis')] = x });
+ var { '_mangleThis': x } = y;
+ var { ['_mangleThis']: x } = y;
+ var { [(z, '_mangleThis')]: x } = y;
+ '_mangleThis' in x;
+ (y ? '_mangleThis' : z) in x;
+ (y ? z : '_mangleThis') in x;
+ (y, '_mangleThis') in x;
+ `,
+ },
+ entryPoints: ["/keep.js", "/mangle.js"],
+ mangleProps: /_/,
+ mode: "passthrough",
+ });
+ itBundled("default/MangleQuotedPropsMinifySyntax", {
+ // GENERATED
+ files: {
+ "/keep.js": /* js */ `
+ foo("_keepThisProperty");
+ foo((x, "_keepThisProperty"));
+ foo(x ? "_keepThisProperty" : "_keepThisPropertyToo");
+ x[foo("_keepThisProperty")];
+ x?.[foo("_keepThisProperty")];
+ ({ [foo("_keepThisProperty")]: x });
+ (class { [foo("_keepThisProperty")] = x });
+ var { [foo("_keepThisProperty")]: x } = y;
+ foo("_keepThisProperty") in x;
+ `,
+ "/mangle.js": /* js */ `
+ x['_mangleThis'];
+ x?.['_mangleThis'];
+ x[y ? '_mangleThis' : z];
+ x?.[y ? '_mangleThis' : z];
+ x[y ? z : '_mangleThis'];
+ x?.[y ? z : '_mangleThis'];
+ x[y, '_mangleThis'];
+ x?.[y, '_mangleThis'];
+ ({ '_mangleThis': x });
+ ({ ['_mangleThis']: x });
+ ({ [(y, '_mangleThis')]: x });
+ (class { '_mangleThis' = x });
+ (class { ['_mangleThis'] = x });
+ (class { [(y, '_mangleThis')] = x });
+ var { '_mangleThis': x } = y;
+ var { ['_mangleThis']: x } = y;
+ var { [(z, '_mangleThis')]: x } = y;
+ '_mangleThis' in x;
+ (y ? '_mangleThis' : z) in x;
+ (y ? z : '_mangleThis') in x;
+ (y, '_mangleThis') in x;
+ `,
+ },
+ entryPoints: ["/keep.js", "/mangle.js"],
+ mangleProps: /_/,
+ mangleQuoted: true,
+ mode: "passthrough",
+ });
+ itBundled("default/IndirectRequireMessage", {
+ // GENERATED
+ files: {
+ "/array.js": `let x = [require]`,
+ "/assign.js": `require = x`,
+ "/ident.js": `let x = require`,
+ "/dot.js": `let x = require.cache`,
+ "/index.js": `let x = require[cache]`,
+ },
+ entryPoints: ["/array.js", "/assign.js", "/dot.js", "/ident.js", "/index.js"],
+ /* TODO FIX expectedScanLog: `array.js: DEBUG: Indirect calls to "require" will not be bundled
+ assign.js: DEBUG: Indirect calls to "require" will not be bundled
+ ident.js: DEBUG: Indirect calls to "require" will not be bundled
+ `, */
+ });
+ itBundled("default/AmbiguousReexportMsg", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ export * from './a'
+ export * from './b'
+ export * from './c'
+ `,
+ "/a.js": `export let a = 1, x = 2`,
+ "/b.js": `export let b = 3; export { b as x }`,
+ "/c.js": `export let c = 4, x = 5`,
+ },
+ /* TODO FIX expectedCompileLog: `DEBUG: Re-export of "x" in "entry.js" is ambiguous and has been removed
+ a.js: NOTE: One definition of "x" comes from "a.js" here:
+ b.js: NOTE: Another definition of "x" comes from "b.js" here:
+ `, */
+ });
+ itBundled("default/NonDeterminismIssue2537", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ export function aap(noot: boolean, wim: number) {
+ let mies = "teun"
+ if (noot) {
+ function vuur(v: number) {
+ return v * 2
+ }
+ function schaap(s: number) {
+ return s / 2
+ }
+ mies = vuur(wim) + schaap(wim)
+ }
+ return mies
+ }
+ `,
+ "/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "alwaysStrict": true
+ }
+ }
+ `,
+ },
+ });
+ itBundled("default/MinifiedJSXPreserveWithObjectSpread", {
+ // GENERATED
+ files: {
+ "/entry.jsx": /* jsx */ `
+ const obj = {
+ before,
+ ...{ [key]: value },
+ ...{ key: value },
+ after,
+ };
+ <Foo
+ before
+ {...{ [key]: value }}
+ {...{ key: value }}
+ after
+ />;
+ <Bar
+ {...{
+ a,
+ [b]: c,
+ ...d,
+ e,
+ }}
+ />;
+ `,
+ },
+ minifySyntax: true,
+ });
+ itBundled("default/PackageAlias", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import "pkg1"
+ import "pkg2/foo"
+ import "./nested3"
+ import "@scope/pkg4"
+ import "@scope/pkg5/foo"
+ import "@abs-path/pkg6"
+ import "@abs-path/pkg7/foo"
+ import "@scope-only/pkg8"
+ import "slash/"
+ import "prefix-foo"
+ import "@scope/prefix-foo"
+ `,
+ "/nested3/index.js": `import "pkg3"`,
+ "/nested3/node_modules/alias3/index.js": `test failure`,
+ "/node_modules/alias1/index.js": `console.log(1)`,
+ "/node_modules/alias2/foo.js": `console.log(2)`,
+ "/node_modules/alias3/index.js": `console.log(3)`,
+ "/node_modules/alias4/index.js": `console.log(4)`,
+ "/node_modules/alias5/foo.js": `console.log(5)`,
+ "/alias6/dir/index.js": `console.log(6)`,
+ "/alias7/dir/foo/index.js": `console.log(7)`,
+ "/alias8/dir/pkg8/index.js": `console.log(8)`,
+ "/alias9/some/file.js": `console.log(9)`,
+ "/node_modules/prefix-foo/index.js": `console.log(10)`,
+ "/node_modules/@scope/prefix-foo/index.js": `console.log(11)`,
+ },
+ });
+ itBundled("default/PackageAliasMatchLongest", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import "pkg"
+ import "pkg/foo"
+ import "pkg/foo/bar"
+ import "pkg/foo/bar/baz"
+ import "pkg/bar/baz"
+ import "pkg/baz"
+ `,
+ },
+ alias: {
+ pkg: "alias/pkg",
+ "pkg/foo": "alias/pkg_foo",
+ "pkg/foo/bar": "alias/pkg_foo_bar",
+ },
+ });
+ itBundled("default/ErrorsForAssertTypeJSON", {
+ // GENERATED
+ files: {
+ "/js-entry.js": /* js */ `
+ import all from './foo.json' assert { type: 'json' }
+ import { default as def } from './foo.json' assert { type: 'json' }
+ import { unused } from './foo.json' assert { type: 'json' }
+ import { used } from './foo.json' assert { type: 'json' }
+ import * as ns from './foo.json' assert { type: 'json' }
+ use(used, ns.prop)
+ export { exported } from './foo.json' assert { type: 'json' }
+ import text from './foo.text' assert { type: 'json' }
+ import file from './foo.file' assert { type: 'json' }
+ import copy from './foo.copy' assert { type: 'json' }
+ `,
+ "/ts-entry.ts": /* ts */ `
+ import all from './foo.json' assert { type: 'json' }
+ import { default as def } from './foo.json' assert { type: 'json' }
+ import { unused } from './foo.json' assert { type: 'json' }
+ import { used } from './foo.json' assert { type: 'json' }
+ import * as ns from './foo.json' assert { type: 'json' }
+ use(used, ns.prop)
+ export { exported } from './foo.json' assert { type: 'json' }
+ import text from './foo.text' assert { type: 'json' }
+ import file from './foo.file' assert { type: 'json' }
+ import copy from './foo.copy' assert { type: 'json' }
+ `,
+ "/foo.json": `{}`,
+ "/foo.text": `{}`,
+ "/foo.file": `{}`,
+ "/foo.copy": `{}`,
+ },
+ entryPoints: ["/js-entry.js", "/ts-entry.ts"],
+ /* TODO FIX expectedScanLog: `js-entry.js: ERROR: Cannot use non-default import "unused" with a standard JSON module
+ js-entry.js: NOTE: This is considered an import of a standard JSON module because of the import assertion here:
+ NOTE: You can either keep the import assertion and only use the "default" import, or you can remove the import assertion and use the "unused" import (which is non-standard behavior).
+ js-entry.js: ERROR: Cannot use non-default import "used" with a standard JSON module
+ js-entry.js: NOTE: This is considered an import of a standard JSON module because of the import assertion here:
+ NOTE: You can either keep the import assertion and only use the "default" import, or you can remove the import assertion and use the "used" import (which is non-standard behavior).
+ js-entry.js: WARNING: Non-default import "prop" is undefined with a standard JSON module
+ js-entry.js: NOTE: This is considered an import of a standard JSON module because of the import assertion here:
+ NOTE: You can either keep the import assertion and only use the "default" import, or you can remove the import assertion and use the "prop" import (which is non-standard behavior).
+ js-entry.js: ERROR: Cannot use non-default import "exported" with a standard JSON module
+ js-entry.js: NOTE: This is considered an import of a standard JSON module because of the import assertion here:
+ NOTE: You can either keep the import assertion and only use the "default" import, or you can remove the import assertion and use the "exported" import (which is non-standard behavior).
+ js-entry.js: ERROR: The file "foo.text" was loaded with the "text" loader
+ js-entry.js: NOTE: This import assertion requires the loader to be "json" instead:
+ NOTE: You need to either reconfigure esbuild to ensure that the loader for this file is "json" or you need to remove this import assertion.
+ js-entry.js: ERROR: The file "foo.file" was loaded with the "file" loader
+ js-entry.js: NOTE: This import assertion requires the loader to be "json" instead:
+ NOTE: You need to either reconfigure esbuild to ensure that the loader for this file is "json" or you need to remove this import assertion.
+ ts-entry.ts: ERROR: Cannot use non-default import "used" with a standard JSON module
+ ts-entry.ts: NOTE: This is considered an import of a standard JSON module because of the import assertion here:
+ NOTE: You can either keep the import assertion and only use the "default" import, or you can remove the import assertion and use the "used" import (which is non-standard behavior).
+ ts-entry.ts: WARNING: Non-default import "prop" is undefined with a standard JSON module
+ ts-entry.ts: NOTE: This is considered an import of a standard JSON module because of the import assertion here:
+ NOTE: You can either keep the import assertion and only use the "default" import, or you can remove the import assertion and use the "prop" import (which is non-standard behavior).
+ ts-entry.ts: ERROR: Cannot use non-default import "exported" with a standard JSON module
+ ts-entry.ts: NOTE: This is considered an import of a standard JSON module because of the import assertion here:
+ NOTE: You can either keep the import assertion and only use the "default" import, or you can remove the import assertion and use the "exported" import (which is non-standard behavior).
+ `, */
+ });
+ itBundled("default/OutputForAssertTypeJSON", {
+ // GENERATED
+ files: {
+ "/js-entry.js": /* js */ `
+ import all from './foo.json' assert { type: 'json' }
+ import copy from './foo.copy' assert { type: 'json' }
+ import { default as def } from './foo.json' assert { type: 'json' }
+ import * as ns from './foo.json' assert { type: 'json' }
+ use(all, copy, def, ns.prop)
+ export { default } from './foo.json' assert { type: 'json' }
+ `,
+ "/ts-entry.ts": /* ts */ `
+ import all from './foo.json' assert { type: 'json' }
+ import copy from './foo.copy' assert { type: 'json' }
+ import { default as def } from './foo.json' assert { type: 'json' }
+ import { unused } from './foo.json' assert { type: 'json' }
+ import * as ns from './foo.json' assert { type: 'json' }
+ use(all, copy, def, ns.prop)
+ export { default } from './foo.json' assert { type: 'json' }
+ `,
+ "/foo.json": `{}`,
+ "/foo.copy": `{}`,
+ },
+ entryPoints: ["/js-entry.js", "/ts-entry.ts"],
+ /* TODO FIX expectedScanLog: `js-entry.js: WARNING: Non-default import "prop" is undefined with a standard JSON module
+ js-entry.js: NOTE: This is considered an import of a standard JSON module because of the import assertion here:
+ NOTE: You can either keep the import assertion and only use the "default" import, or you can remove the import assertion and use the "prop" import (which is non-standard behavior).
+ ts-entry.ts: WARNING: Non-default import "prop" is undefined with a standard JSON module
+ ts-entry.ts: NOTE: This is considered an import of a standard JSON module because of the import assertion here:
+ NOTE: You can either keep the import assertion and only use the "default" import, or you can remove the import assertion and use the "prop" import (which is non-standard behavior).
+ `, */
+ });
+ itBundled("default/ExternalPackages", {
+ // GENERATED
+ files: {
+ "/project/entry.js": /* js */ `
+ import 'pkg1'
+ import './file'
+ import './node_modules/pkg2/index.js'
+ import '#pkg3'
+ `,
+ "/project/package.json": /* json */ `
+ {
+ "imports": {
+ "#pkg3": "./libs/pkg3.js"
+ }
+ }
+ `,
+ "/project/file.js": `console.log('file')`,
+ "/project/node_modules/pkg2/index.js": `console.log('pkg2')`,
+ "/project/libs/pkg3.js": `console.log('pkg3')`,
+ },
+ });
+ itBundled("default/MetafileVariousCases", {
+ // GENERATED
+ files: {
+ "/project/entry.js": /* js */ `
+ import a from 'extern-esm'
+ import b from './esm'
+ import c from 'data:application/json,2'
+ import d from './file.file'
+ import e from './copy.copy'
+ console.log(
+ a,
+ b,
+ c,
+ d,
+ e,
+ require('extern-cjs'),
+ require('./cjs'),
+ import('./dynamic'),
+ )
+ export let exported
+ `,
+ "/project/entry.css": /* css */ `
+ @import "extern.css";
+ a { background: url(inline.svg) }
+ b { background: url(file.file) }
+ c { background: url(copy.copy) }
+ d { background: url(extern.png) }
+ `,
+ "/project/esm.js": `export default 1`,
+ "/project/cjs.js": `module.exports = 4`,
+ "/project/dynamic.js": `export default 5`,
+ "/project/file.file": `file`,
+ "/project/copy.copy": `copy`,
+ "/project/inline.svg": `<svg/>`,
+ },
+ entryPoints: ["/project/entry.js", "/project/entry.css"],
+ loader: {
+ ".js": "js",
+ ".css": "css",
+ ".file": "file",
+ ".copy": "copy",
+ ".svg": "dataurl",
+ },
+ external: ["extern-esm", "extern-cjs", "extern.css", "extern.png"],
+ metafile: true,
+ });
+ itBundled("default/MetafileNoBundle", {
+ // GENERATED
+ files: {
+ "/project/entry.js": /* js */ `
+ import a from 'pkg'
+ import b from './file'
+ console.log(
+ a,
+ b,
+ require('pkg2'),
+ require('./file2'),
+ import('./dynamic'),
+ )
+ export let exported
+ `,
+ "/project/entry.css": /* css */ `
+ @import "pkg";
+ @import "./file";
+ a { background: url(pkg2) }
+ a { background: url(./file2) }
+ `,
+ },
+ entryPoints: ["/project/entry.js", "/project/entry.css"],
+ mode: "convertformat",
+ });
+ itBundled("default/MetafileVeryLongExternalPaths", {
+ // GENERATED
+ files: {
+ "/project/bytesInOutput should be at least 99 (1).js": /* js */ `
+ import a from './\` + strings.Repeat("1", 99) + \`.file'
+ console.log(a)
+ `,
+ "/project/bytesInOutput should be at least 99 (2).js": /* js */ `
+ import a from './\` + strings.Repeat("2", 99) + \`.copy'
+ console.log(a)
+ `,
+ "/project/bytesInOutput should be at least 99 (3).js": `import('./\` + strings.Repeat("3", 99) + \`.js').then(console.log)`,
+ "/project/bytesInOutput should be at least 99.css": `a { background: url(\` + strings.Repeat("4", 99) + \`.file) }`,
+ },
+ entryPoints: [
+ "/project/bytesInOutput should be at least 99 (1).js",
+ "/project/bytesInOutput should be at least 99 (2).js",
+ "/project/bytesInOutput should be at least 99 (3).js",
+ "/project/bytesInOutput should be at least 99.css",
+ ],
+ metafile: true,
+ loader: {
+ ".js": "js",
+ ".css": "css",
+ ".file": "file",
+ ".copy": "copy",
+ },
+ });
+ itBundled("default/CommentPreservation", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ console.log(
+ import(/* before */ foo),
+ import(/* before */ 'foo'),
+ import(foo /* after */),
+ import('foo' /* after */),
+ )
+
+ console.log(
+ import('foo', /* before */ { assert: { type: 'json' } }),
+ import('foo', { /* before */ assert: { type: 'json' } }),
+ import('foo', { assert: /* before */ { type: 'json' } }),
+ import('foo', { assert: { /* before */ type: 'json' } }),
+ import('foo', { assert: { type: /* before */ 'json' } }),
+ import('foo', { assert: { type: 'json' /* before */ } }),
+ import('foo', { assert: { type: 'json' } /* before */ }),
+ import('foo', { assert: { type: 'json' } } /* before */),
+ )
+
+ console.log(
+ require(/* before */ foo),
+ require(/* before */ 'foo'),
+ require(foo /* after */),
+ require('foo' /* after */),
+ )
+
+ console.log(
+ require.resolve(/* before */ foo),
+ require.resolve(/* before */ 'foo'),
+ require.resolve(foo /* after */),
+ require.resolve('foo' /* after */),
+ )
+
+ let [/* foo */] = [/* bar */];
+ let [
+ // foo
+ ] = [
+ // bar
+ ];
+ let [/*before*/ ...s] = [/*before*/ ...s]
+ let [... /*before*/ s2] = [... /*before*/ s2]
+
+ let { /* foo */ } = { /* bar */ };
+ let {
+ // foo
+ } = {
+ // bar
+ };
+ let { /*before*/ ...s3 } = { /*before*/ ...s3 }
+ let { ... /*before*/ s4 } = { ... /*before*/ s4 }
+
+ let [/* before */ x] = [/* before */ x];
+ let [/* before */ x2 /* after */] = [/* before */ x2 /* after */];
+ let [
+ // before
+ x3
+ // after
+ ] = [
+ // before
+ x3
+ // after
+ ];
+
+ let { /* before */ y } = { /* before */ y };
+ let { /* before */ y2 /* after */ } = { /* before */ y2 /* after */ };
+ let {
+ // before
+ y3
+ // after
+ } = {
+ // before
+ y3
+ // after
+ };
+ let { /* before */ [y4]: y4 } = { /* before */ [y4]: y4 };
+ let { [/* before */ y5]: y5 } = { [/* before */ y5]: y5 };
+ let { [y6 /* after */]: y6 } = { [y6 /* after */]: y6 };
+
+ foo[/* before */ x] = foo[/* before */ x]
+ foo[x /* after */] = foo[x /* after */]
+
+ console.log(
+ // before
+ foo,
+ /* comment before */
+ bar,
+ // comment after
+ )
+
+ console.log([
+ // before
+ foo,
+ /* comment before */
+ bar,
+ // comment after
+ ])
+
+ console.log({
+ // before
+ foo,
+ /* comment before */
+ bar,
+ // comment after
+ })
+
+ console.log(class {
+ // before
+ foo
+ /* comment before */
+ bar
+ // comment after
+ })
+
+ console.log(
+ () => { return /* foo */ null },
+ () => { throw /* foo */ null },
+ () => { return (/* foo */ null) + 1 },
+ () => { throw (/* foo */ null) + 1 },
+ () => {
+ return (// foo
+ null) + 1
+ },
+ () => {
+ throw (// foo
+ null) + 1
+ },
+ )
+
+ console.log(
+ /*a*/ a ? /*b*/ b : /*c*/ c,
+ a /*a*/ ? b /*b*/ : c /*c*/,
+ )
+
+ for (/*foo*/a;;);
+ for (;/*foo*/a;);
+ for (;;/*foo*/a);
+
+ for (/*foo*/a in b);
+ for (a in /*foo*/b);
+
+ for (/*foo*/a of b);
+ for (a of /*foo*/b);
+
+ if (/*foo*/a);
+ with (/*foo*/a);
+ while (/*foo*/a);
+ do {} while (/*foo*/a);
+ switch (/*foo*/a) {}
+ `,
+ },
+ format: "cjs",
+ });
+ itBundled("default/CommentPreservationImportAssertions", {
+ // GENERATED
+ files: {
+ "/entry.jsx": /* jsx */ `
+ import 'foo' /* before */ assert { type: 'json' }
+ import 'foo' assert /* before */ { type: 'json' }
+ import 'foo' assert { /* before */ type: 'json' }
+ import 'foo' assert { type: /* before */ 'json' }
+ import 'foo' assert { type: 'json' /* before */ }
+ `,
+ },
+ });
+ itBundled("default/CommentPreservationTransformJSX", {
+ // GENERATED
+ files: {
+ "/entry.jsx": /* jsx */ `
+ console.log(
+ <div x={/*before*/x} />,
+ <div x={/*before*/'y'} />,
+ <div x={/*before*/true} />,
+ <div {/*before*/...x} />,
+ <div>{/*before*/x}</div>,
+ <>{/*before*/x}</>,
+
+ // Comments on absent AST nodes
+ <div>before{}after</div>,
+ <div>before{/* comment 1 *//* comment 2 */}after</div>,
+ <div>before{
+ // comment 1
+ // comment 2
+ }after</div>,
+ <>before{}after</>,
+ <>before{/* comment 1 *//* comment 2 */}after</>,
+ <>before{
+ // comment 1
+ // comment 2
+ }after</>,
+ )
+ `,
+ },
+ });
+ itBundled("default/CommentPreservationPreserveJSX", {
+ // GENERATED
+ files: {
+ "/entry.jsx": /* jsx */ `
+ console.log(
+ <div x={/*before*/x} />,
+ <div x={/*before*/'y'} />,
+ <div x={/*before*/true} />,
+ <div {/*before*/...x} />,
+ <div>{/*before*/x}</div>,
+ <>{/*before*/x}</>,
+
+ // Comments on absent AST nodes
+ <div>before{}after</div>,
+ <div>before{/* comment 1 *//* comment 2 */}after</div>,
+ <div>before{
+ // comment 1
+ // comment 2
+ }after</div>,
+ <>before{}after</>,
+ <>before{/* comment 1 *//* comment 2 */}after</>,
+ <>before{
+ // comment 1
+ // comment 2
+ }after</>,
+ )
+ `,
+ },
+ });
+ itBundled("default/ErrorMessageCrashStdinIssue2913", {
+ // GENERATED
+ files: {
+ "/project/node_modules/fflate/package.json": `{ "main": "main.js" }`,
+ "/project/node_modules/fflate/main.js": ``,
+ },
+ stdin: {
+ contents: `import "node_modules/fflate"`,
+ cwd: "/project",
+ },
+ platform: "neutral",
+ /* TODO FIX expectedScanLog: `<stdin>: ERROR: Could not resolve "node_modules/fflate"
+ NOTE: You can mark the path "node_modules/fflate" as external to exclude it from the bundle, which will remove this error.
+ `, */
+ });
+});
diff --git a/test/bundler/esbuild/importstar.test.ts b/test/bundler/esbuild/importstar.test.ts
new file mode 100644
index 000000000..8231b7043
--- /dev/null
+++ b/test/bundler/esbuild/importstar.test.ts
@@ -0,0 +1,1346 @@
+import assert from "assert";
+import { expectBundled, itBundled, testForFile } from "../expectBundled";
+var { describe, test, expect } = testForFile(import.meta.path);
+
+// Tests ported from:
+// https://github.com/evanw/esbuild/blob/main/internal/bundler_tests/bundler_importstar_test.go
+
+// For debug, all files are written to $TEMP/bun-bundle-tests/importstar
+
+describe("bundler", () => {
+ itBundled("importstar/ImportStarUnused", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './foo'
+ let foo = 234
+ console.log(foo)
+ `,
+ "/foo.js": `export const foo = "FAIL"`,
+ },
+ dce: true,
+ run: {
+ stdout: "234",
+ },
+ });
+ itBundled("importstar/ImportStarCapture", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './foo'
+ let foo = 234
+ console.log(JSON.stringify(ns), ns.foo, foo)
+ `,
+ "/foo.js": `export const foo = 123`,
+ },
+ run: {
+ stdout: '{"foo":123} 123 234',
+ },
+ });
+ itBundled("importstar/ImportStarNoCapture", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './foo'
+ let foo = 234
+ console.log(ns.foo, ns.foo, foo)
+ `,
+ "/foo.js": `export const foo = 123`,
+ },
+ run: {
+ stdout: "123 123 234",
+ },
+ });
+ itBundled("importstar/ImportStarExportImportStarUnused", {
+ files: {
+ "/entry.js": /* js */ `
+ import {ns} from './bar'
+ let foo = 234
+ console.log(foo)
+ `,
+ "/foo.js": `export const foo = 123; export const bar = "FAILED";`,
+ "/bar.js": /* js */ `
+ import * as ns from './foo'
+ export {ns}
+ `,
+ },
+ dce: true,
+ run: {
+ stdout: "234",
+ },
+ });
+ itBundled("importstar/ImportStarExportImportStarNoCapture", {
+ files: {
+ "/entry.js": /* js */ `
+ import {ns} from './bar'
+ let foo = 234
+ console.log(ns.foo, ns.foo, foo)
+ `,
+ "/foo.js": `export const foo = 123`,
+ "/bar.js": /* js */ `
+ import * as ns from './foo'
+ export {ns}
+ `,
+ },
+ run: {
+ stdout: "123 123 234",
+ },
+ });
+ itBundled("importstar/ImportStarExportImportStarCapture", {
+ files: {
+ "/entry.js": /* js */ `
+ import {ns} from './bar'
+ let foo = 234
+ console.log(JSON.stringify(ns), ns.foo, foo)
+ `,
+ "/foo.js": `export const foo = 123`,
+ "/bar.js": /* js */ `
+ import * as ns from './foo'
+ export {ns}
+ `,
+ },
+ run: {
+ stdout: '{"foo":123} 123 234',
+ },
+ });
+ itBundled("importstar/ImportStarExportStarAsUnused", {
+ files: {
+ "/entry.js": /* js */ `
+ import {ns} from './bar'
+ let foo = 234
+ console.log(foo)
+ `,
+ "/foo.js": `export const foo = "FAILED"`,
+ "/bar.js": `export * as ns from './foo'`,
+ },
+ dce: true,
+ run: {
+ stdout: "234",
+ },
+ });
+ itBundled("importstar/ImportStarExportStarAsNoCapture", {
+ files: {
+ "/entry.js": /* js */ `
+ import {ns} from './bar'
+ let foo = 234
+ console.log(ns.foo, ns.foo, foo)
+ `,
+ "/foo.js": `export const foo = 123`,
+ "/bar.js": `export * as ns from './foo'`,
+ },
+ run: {
+ stdout: "123 123 234",
+ },
+ });
+ itBundled("importstar/ImportStarExportStarAsCapture", {
+ files: {
+ "/entry.js": /* js */ `
+ import {ns} from './bar'
+ let foo = 234
+ console.log(JSON.stringify(ns), ns.foo, foo)
+ `,
+ "/foo.js": `export const foo = 123`,
+ "/bar.js": `export * as ns from './foo'`,
+ },
+ run: {
+ stdout: '{"foo":123} 123 234',
+ },
+ });
+ itBundled("importstar/ImportStarExportStarUnused", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './bar'
+ let foo = 234
+ console.log(foo)
+ `,
+ "/foo.js": `export const foo = "FAILED"`,
+ "/bar.js": `export * from './foo'`,
+ },
+ dce: true,
+ run: {
+ stdout: "234",
+ },
+ });
+ itBundled("importstar/ImportStarExportStarNoCapture", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './bar'
+ let foo = 234
+ console.log(ns.foo, ns.foo, foo)
+ `,
+ "/foo.js": `export const foo = 123`,
+ "/bar.js": `export * from './foo'`,
+ },
+ run: {
+ stdout: "123 123 234",
+ },
+ });
+ itBundled("importstar/ImportStarExportStarCapture", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './bar'
+ let foo = 234
+ console.log(JSON.stringify(ns), ns.foo, foo)
+ `,
+ "/foo.js": `export const foo = 123`,
+ "/bar.js": `export * from './foo'`,
+ },
+ run: {
+ stdout: '{"foo":123} 123 234',
+ },
+ });
+ itBundled("importstar/ImportStarCommonJSUnused", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './foo'
+ let foo = 234
+ console.log(foo)
+ `,
+ "/foo.js": `exports.foo = 123`,
+ },
+ run: {
+ stdout: "234",
+ },
+ });
+ itBundled("importstar/ImportStarCommonJSCapture", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './foo'
+ let foo = 234
+ console.log(JSON.stringify(ns), ns.foo, foo)
+ `,
+ "/foo.js": `exports.foo = 123`,
+ },
+ run: {
+ stdout: '{"default":{"foo":123},"foo":123} 123 234',
+ },
+ });
+ itBundled("importstar/ImportStarCommonJSNoCapture", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './foo'
+ let foo = 234
+ console.log(ns.foo, ns.foo, foo)
+ `,
+ "/foo.js": `exports.foo = 123`,
+ },
+ run: {
+ stdout: "123 123 234",
+ },
+ });
+ itBundled("importstar/ImportStarAndCommonJS", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './foo'
+ const ns2 = require('./foo')
+ console.log(ns.foo, ns2.foo)
+ `,
+ "/foo.js": `export const foo = 123`,
+ },
+ });
+ itBundled("importstar/ImportStarNoBundleUnused", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './foo'
+ let foo = 234
+ console.log(foo)
+ `,
+ },
+ mode: "transform",
+ runtimeFiles: {
+ "/foo.js": `console.log('foo')`,
+ },
+ run: {
+ stdout: "foo\n234",
+ },
+ });
+ itBundled("importstar/ImportStarNoBundleCapture", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './foo'
+ let foo = 234
+ console.log(JSON.stringify(ns), ns.foo, foo)
+ `,
+ },
+ mode: "transform",
+ runtimeFiles: {
+ "/foo.js": `export const foo = 123`,
+ },
+ run: {
+ stdout: '{"foo":123} 123 234',
+ },
+ });
+ itBundled("importstar/ImportStarNoBundleNoCapture", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './foo'
+ let foo = 234
+ console.log(ns.foo, ns.foo, foo)
+ `,
+ },
+ mode: "transform",
+ runtimeFiles: {
+ "/foo.js": `export const foo = 123`,
+ },
+ run: {
+ stdout: "123 123 234",
+ },
+ });
+ itBundled("importstar/ImportStarMangleNoBundleUnused", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './foo'
+ let foo = 234
+ console.log(foo)
+ `,
+ },
+ minifySyntax: true,
+ mode: "transform",
+ runtimeFiles: {
+ "/foo.js": `console.log('foo')`,
+ },
+ run: {
+ stdout: "foo\n234",
+ },
+ });
+ itBundled("importstar/ImportStarMangleNoBundleCapture", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './foo'
+ let foo = 234
+ console.log(JSON.stringify(ns), ns.foo, foo)
+ `,
+ },
+ minifySyntax: true,
+ mode: "transform",
+ runtimeFiles: {
+ "/foo.js": `export const foo = 123`,
+ },
+ run: {
+ stdout: '{"foo":123} 123 234',
+ },
+ });
+ itBundled("importstar/ImportStarMangleNoBundleNoCapture", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './foo'
+ let foo = 234
+ console.log(JSON.stringify(ns), ns.foo, foo)
+ `,
+ },
+ minifySyntax: true,
+ mode: "transform",
+ runtimeFiles: {
+ "/foo.js": `export const foo = 123`,
+ },
+ run: {
+ stdout: '{"foo":123} 123 234',
+ },
+ });
+ itBundled("importstar/ImportStarExportStarOmitAmbiguous", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './common'
+ console.log(JSON.stringify(ns))
+ `,
+ "/common.js": /* js */ `
+ export * from './foo'
+ export * from './bar'
+ `,
+ "/foo.js": /* js */ `
+ export const x = 1
+ export const y = "FAILED"
+ `,
+ "/bar.js": /* js */ `
+ export const y = "FAILED"
+ export const z = 4
+ `,
+ },
+ dce: true,
+ run: {
+ stdout: '{"x":1,"z":4}',
+ },
+ });
+ itBundled("importstar/ImportExportStarAmbiguousError", {
+ files: {
+ "/entry.js": /* js */ `
+ import {x, y, z} from './common'
+ console.log(x, y, z)
+ `,
+ "/common.js": /* js */ `
+ export * from './foo'
+ export * from './bar'
+ `,
+ "/foo.js": /* js */ `
+ export const x = 1
+ export const y = 2
+ `,
+ "/bar.js": /* js */ `
+ export const y = 3
+ export const z = 4
+ `,
+ },
+ bundleErrors: {
+ "/entry.js": ['Ambiguous import "y" has multiple matching exports'],
+ },
+ });
+ itBundled("importstar/ImportExportStarAmbiguous", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './common'
+ console.log(ns.x, ns.y, ns.z)
+ `,
+ "/common.js": /* js */ `
+ export * from './foo'
+ export * from './bar'
+ `,
+ "/foo.js": /* js */ `
+ export const x = 1
+ export const y = 2
+ `,
+ "/bar.js": /* js */ `
+ export const y = 3
+ export const z = 4
+ `,
+ },
+ bundleWarnings: {
+ "/entry.js": ['Import "y" will always be undefined because there are multiple matching exports'],
+ },
+ run: {
+ stdout: "1 undefined 4",
+ },
+ });
+ itBundled("importstar/ReExportStarNameCollisionNotAmbiguousImport", {
+ files: {
+ "/entry.js": /* js */ `
+ import {x, y} from './common'
+ console.log(x, y)
+ `,
+ "/common.js": /* js */ `
+ export * from './a'
+ export * from './b'
+ `,
+ "/a.js": `export * from './c'`,
+ "/b.js": `export {x} from './c'`,
+ "/c.js": `export let x = 1, y = 2`,
+ },
+ run: {
+ stdout: "1 2",
+ },
+ });
+ itBundled("importstar/ReExportStarNameCollisionNotAmbiguousExport", {
+ files: {
+ "/entry.js": /* js */ `
+ export * from './a'
+ export * from './b'
+ `,
+ "/a.js": `export * from './c'`,
+ "/b.js": `export {x} from './c'`,
+ "/c.js": `export let x = 1, y = 2`,
+ },
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ import {x, y} from './entry.js'
+ import assert from 'assert'
+ assert.strictEqual(x, 1)
+ assert.strictEqual(y, 2)
+ `,
+ },
+ run: { file: "/test.js" },
+ });
+ itBundled("importstar/ReExportStarNameShadowingNotAmbiguous", {
+ files: {
+ "/entry.js": /* js */ `
+ import {x} from './a'
+ console.log(x)
+ `,
+ "/a.js": /* js */ `
+ export * from './b'
+ export let x = 1
+ `,
+ "/b.js": `export let x = "FAILED"`,
+ },
+ dce: true,
+ run: {
+ stdout: "1",
+ },
+ });
+ itBundled("importstar/ReExportStarNameShadowingNotAmbiguousReExport", {
+ files: {
+ "/entry.js": /* js */ `
+ import {x} from './a'
+ console.log(x)
+ `,
+ "/a.js": `export * from './b'`,
+ "/b.js": /* js */ `
+ export * from './c'
+ export let x = 1
+ `,
+ "/c.js": `export let x = "FAILED"`,
+ },
+ dce: true,
+ run: {
+ stdout: "1",
+ },
+ });
+ itBundled("importstar/ImportStarOfExportStarAs", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as foo_ns from './foo'
+ console.log(JSON.stringify(foo_ns))
+ `,
+ "/foo.js": `export * as bar_ns from './bar'`,
+ "/bar.js": `export const bar = 123`,
+ },
+ run: {
+ stdout: '{"bar_ns":{"bar":123}}',
+ },
+ });
+ itBundled("importstar/ImportOfExportStar", {
+ files: {
+ "/entry.js": /* js */ `
+ import {bar} from './foo'
+ console.log(bar)
+ `,
+ "/foo.js": `export * from './bar'`,
+ "/bar.js": /* js */ `
+ // Add some statements to increase the part index (this reproduced a crash)
+ statement()
+ statement()
+ statement()
+ statement()
+ export const bar = 123
+ `,
+ },
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ globalThis.statement = () => {}
+ await import('./out.js')
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: "123",
+ },
+ });
+ itBundled("importstar/ImportOfExportStarOfImport", {
+ files: {
+ "/entry.js": /* js */ `
+ import {bar} from './foo'
+ console.log(bar)
+ `,
+ "/foo.js": /* js */ `
+ // Add some statements to increase the part index (this reproduced a crash)
+ statement()
+ statement()
+ statement()
+ statement()
+ export * from './bar'
+ `,
+ "/bar.js": `export {value as bar} from './baz'`,
+ "/baz.js": `export const value = 123`,
+ },
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ globalThis.statement = () => {}
+ await import('./out.js')
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: "123",
+ },
+ });
+ itBundled("importstar/ExportSelfIIFE", {
+ files: {
+ "/entry.js": /* js */ `
+ export const foo = 123
+ export * from './entry'
+ `,
+ },
+ format: "iife",
+ });
+ itBundled("importstar/ExportSelfIIFEWithName", {
+ files: {
+ "/entry.js": /* js */ `
+ export const foo = 123
+ export * from './entry'
+ `,
+ },
+ format: "iife",
+ globalName: "someName",
+ onAfterBundle(api) {
+ api.appendFile("/out.js", "\nconsole.log(JSON.stringify(someName))");
+ },
+ run: {
+ stdout: '{"foo":123}',
+ },
+ });
+ itBundled("importstar/ExportSelfES6", {
+ files: {
+ "/entry.js": /* js */ `
+ export const foo = 123
+ export * from './entry'
+ `,
+ },
+ format: "esm",
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ import * as foo from './out.js'
+ console.log(JSON.stringify(foo));
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: '{"foo":123}',
+ },
+ });
+ itBundled("importstar/ExportSelfCommonJS", {
+ files: {
+ "/entry.js": /* js */ `
+ export const foo = 123
+ export * from './entry'
+ `,
+ },
+ format: "cjs",
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ console.log(JSON.stringify(require("./out.js")));
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: '{"foo":123}',
+ },
+ });
+ itBundled("importstar/ExportSelfCommonJSMinified", {
+ files: {
+ "/entry.js": /* js */ `
+ module.exports = {foo: 123}
+ console.log(JSON.stringify(require('./entry')))
+ `,
+ },
+ minifyIdentifiers: true,
+ format: "cjs",
+ run: {
+ stdout: '{"foo":123}',
+ },
+ });
+ itBundled("importstar/ImportSelfCommonJS", {
+ files: {
+ "/entry.js": /* js */ `
+ exports.foo = 123
+ import {foo} from './entry'
+ console.log('1', foo)
+ `,
+ },
+ format: "cjs",
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ console.log('2', JSON.stringify(require("./out.js")));
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: '1 undefined\n2 {"foo":123}',
+ },
+ });
+ itBundled("importstar/ExportSelfAsNamespaceES6", {
+ files: {
+ "/entry.js": /* js */ `
+ export const foo = 123
+ export * as ns from './entry'
+ `,
+ },
+ format: "esm",
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ import * as foo from './out.js'
+ console.log(foo.foo, foo.ns.ns.ns.foo, foo.ns.ns === foo.ns);
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: "123 123 true",
+ },
+ });
+ itBundled("importstar/ImportExportSelfAsNamespaceES6", {
+ files: {
+ "/entry.js": /* js */ `
+ export const foo = 123
+ import * as ns from './entry'
+ export {ns}
+ `,
+ },
+ format: "esm",
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ import * as foo from './out.js'
+ console.log(foo.foo, foo.ns.ns.ns.foo, foo.ns.ns === foo.ns);
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: "123 123 true",
+ },
+ });
+ itBundled("importstar/ReExportOtherFileExportSelfAsNamespaceES6", {
+ files: {
+ "/entry.js": `export * from './foo'`,
+ "/foo.js": /* js */ `
+ export const foo = 123
+ export * as ns from './foo'
+ `,
+ },
+ format: "esm",
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ import * as foo from './out.js'
+ console.log(foo.foo, foo.ns.ns.ns.foo, foo.ns.ns === foo.ns);
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: "123 123 true",
+ },
+ });
+ itBundled("importstar/ReExportOtherFileImportExportSelfAsNamespaceES6", {
+ files: {
+ "/entry.js": `export * from './foo'`,
+ "/foo.js": /* js */ `
+ export const foo = 123
+ import * as ns from './foo'
+ export {ns}
+ `,
+ },
+ format: "esm",
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ import * as foo from './out.js'
+ console.log(foo.foo, foo.ns.ns.ns.foo, foo.ns.ns === foo.ns);
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: "123 123 true",
+ },
+ });
+ itBundled("importstar/OtherFileExportSelfAsNamespaceUnusedES6", {
+ files: {
+ "/entry.js": `export {foo} from './foo'`,
+ "/foo.js": /* js */ `
+ export const foo = 123
+ export * as FAILED from './foo'
+ `,
+ },
+ format: "esm",
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ import * as foo from './out.js'
+ console.log(JSON.stringify(foo));
+ `,
+ },
+ dce: true,
+ run: {
+ file: "/test.js",
+ stdout: '{"foo":123}',
+ },
+ });
+ itBundled("importstar/OtherFileImportExportSelfAsNamespaceUnusedES6", {
+ files: {
+ "/entry.js": `export {foo} from './foo'`,
+ "/foo.js": /* js */ `
+ export const foo = 123
+ import * as FAILED from './foo'
+ export {FAILED}
+ `,
+ },
+ format: "esm",
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ import * as foo from './out.js'
+ console.log(JSON.stringify(foo));
+ `,
+ },
+ dce: true,
+ run: {
+ file: "/test.js",
+ stdout: '{"foo":123}',
+ },
+ });
+ itBundled("importstar/ExportSelfAsNamespaceCommonJS", {
+ files: {
+ "/entry.js": /* js */ `
+ export const foo = 123
+ export * as ns from './entry'
+ `,
+ },
+ format: "cjs",
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ const foo = require('./out.js')
+ console.log(foo.foo, foo.ns.ns.ns.foo, foo.ns.ns === foo.ns);
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: "123 123 true",
+ },
+ });
+ itBundled("importstar/ExportSelfAndRequireSelfCommonJS", {
+ files: {
+ "/entry.js": /* js */ `
+ export const foo = 123
+ console.log(JSON.stringify(require('./entry')))
+ `,
+ },
+ format: "cjs",
+ run: {
+ stdout: '{"foo":123}',
+ },
+ });
+ itBundled("importstar/ExportSelfAndImportSelfCommonJS", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as x from './entry'
+ export const foo = 123
+ console.log(JSON.stringify(x))
+ `,
+ },
+ format: "cjs",
+ run: {
+ stdout: '{"foo":123}',
+ },
+ });
+ itBundled("importstar/ExportOtherAsNamespaceCommonJS", {
+ files: {
+ "/entry.js": `export * as ns from './foo'`,
+ "/foo.js": `exports.foo = 123`,
+ },
+ format: "cjs",
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ const foo = require('./out.js')
+ console.log(JSON.stringify(foo));
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: '{"ns":{"default":{"foo":123},"foo":123}}',
+ },
+ });
+ itBundled("importstar/ImportExportOtherAsNamespaceCommonJS", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './foo'
+ export {ns}
+ `,
+ "/foo.js": `exports.foo = 123`,
+ },
+ format: "cjs",
+ });
+ itBundled("importstar/NamespaceImportMissingES6", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './foo'
+ console.log(JSON.stringify(ns), ns.foo)
+ `,
+ "/foo.js": `export const x = 123`,
+ },
+ run: {
+ stdout: '{"x":123} undefined',
+ },
+ });
+ itBundled("importstar/ExportOtherCommonJS", {
+ files: {
+ "/entry.js": `export {bar} from './foo'`,
+ "/foo.js": `exports.foo = 123`,
+ },
+ format: "cjs",
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ const foo = require('./out.js')
+ console.log(...Object.keys(foo));
+ console.log(JSON.stringify(foo));
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: "bar\n{}",
+ },
+ });
+ itBundled("importstar/ExportOtherNestedCommonJS", {
+ files: {
+ "/entry.js": `export {y} from './bar'`,
+ "/bar.js": `export {x as y} from './foo'`,
+ "/foo.js": `exports.foo = 123`,
+ },
+ format: "cjs",
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ const foo = require('./out.js')
+ console.log(...Object.keys(foo));
+ console.log(JSON.stringify(foo));
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: "y\n{}",
+ },
+ });
+ itBundled("importstar/NamespaceImportUnusedMissingES6", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './foo'
+ console.log(ns.foo)
+ `,
+ "/foo.js": `export const x = "FAILED"`,
+ },
+ dce: true,
+ run: {
+ stdout: "undefined",
+ },
+ });
+ itBundled("importstar/NamespaceImportMissingCommonJS", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './foo'
+ console.log(JSON.stringify(ns), ns.foo)
+ `,
+ "/foo.js": `exports.x = 123`,
+ },
+ format: "cjs",
+ run: {
+ stdout: '{"default":{"x":123},"x":123} undefined',
+ },
+ });
+ itBundled("importstar/NamespaceImportUnusedMissingCommonJS", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './foo'
+ console.log(ns.foo)
+ `,
+ "/foo.js": `exports.x = 123`,
+ },
+ run: {
+ stdout: "undefined",
+ },
+ });
+ itBundled("importstar/ReExportNamespaceImportMissingES6", {
+ files: {
+ "/entry.js": /* js */ `
+ import {ns} from './foo'
+ console.log(JSON.stringify(ns), ns.foo)
+ `,
+ "/foo.js": `export * as ns from './bar'`,
+ "/bar.js": `export const x = 123`,
+ },
+ run: {
+ stdout: '{"x":123} undefined',
+ },
+ });
+ itBundled("importstar/ReExportNamespaceImportUnusedMissingES6", {
+ files: {
+ "/entry.js": /* js */ `
+ import {ns} from './foo'
+ console.log(ns.foo)
+ `,
+ "/foo.js": `export * as ns from './bar'`,
+ "/bar.js": `export const x = 123`,
+ },
+ run: {
+ stdout: "undefined",
+ },
+ });
+ itBundled("importstar/NamespaceImportReExportMissingES6", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './foo'
+ console.log(ns, ns.foo)
+ `,
+ "/foo.js": `export {foo} from './bar'`,
+ "/bar.js": `export const x = 123`,
+ },
+ bundleErrors: {
+ "/foo.js": ['ERROR: No matching export in "bar.js" for import "foo"'],
+ },
+ });
+ itBundled("importstar/NamespaceImportReExportUnusedMissingES6", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './foo'
+ console.log(ns.foo)
+ `,
+ "/foo.js": `export {foo} from './bar'`,
+ "/bar.js": `export const x = 123`,
+ },
+ bundleErrors: {
+ "/foo.js": ['ERROR: No matching export in "bar.js" for import "foo"'],
+ },
+ });
+ itBundled("importstar/NamespaceImportReExportStarMissingES6", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './foo'
+ console.log(JSON.stringify(ns), ns.foo)
+ `,
+ "/foo.js": `export * from './bar'`,
+ "/bar.js": `export const x = 123`,
+ },
+ bundleWarnings: {
+ "/entry.js": [`Import "foo" will always be undefined because there is no matching export in "foo.js"`],
+ },
+ run: {
+ stdout: '{"x":123} undefined',
+ },
+ });
+ itBundled("importstar/NamespaceImportReExportStarUnusedMissingES6", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './foo'
+ console.log(ns.foo)
+ `,
+ "/foo.js": `export * from './bar'`,
+ "/bar.js": `export const x = 123`,
+ },
+ bundleWarnings: {
+ "/entry.js": [`Import "foo" will always be undefined because there is no matching export in "foo.js"`],
+ },
+ run: {
+ stdout: "undefined",
+ },
+ });
+ itBundled("importstar/ExportStarDefaultExportCommonJS", {
+ files: {
+ "/entry.js": `export * from './foo'`,
+ "/foo.js": /* js */ `
+ export default 'FAILED' // This should not be picked up
+ export let foo = 'foo'
+ `,
+ },
+ format: "cjs",
+ dce: true,
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ const foo = require('./out.js')
+ console.log(JSON.stringify(foo));
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: '{"foo":"foo"}',
+ },
+ });
+ itBundled("importstar/Issue176", {
+ files: {
+ "/entry.js": /* js */ `
+ import * as things from './folders'
+ console.log(JSON.stringify(things), things.foo())
+ `,
+ "/folders/index.js": `export * from "./child"`,
+ "/folders/child/index.js": `export { foo } from './foo'`,
+ "/folders/child/foo.js": `export const foo = () => 'hi there'`,
+ },
+ run: {
+ stdout: "{} hi there",
+ },
+ });
+ itBundled("importstar/ReExportStarExternalIIFE", {
+ files: {
+ "/entry.js": `export * from "foo"`,
+ },
+ format: "iife",
+ globalName: "mod",
+ external: ["foo"],
+ runtimeFiles: {
+ "/node_modules/foo/index.js": /* js */ `
+ export const foo = 'foo'
+ export const bar = 'bar'
+ `,
+ },
+ onAfterBundle(api) {
+ api.appendFile("/out.js", "\nconsole.log(JSON.stringify(mod))");
+ },
+ run: {
+ stdout: '{"bar":"bar","foo":"foo"}',
+ },
+ });
+ itBundled("importstar/ReExportStarExternalES6", {
+ files: {
+ "/entry.js": `export * from "foo"`,
+ },
+ external: ["foo"],
+ format: "esm",
+ runtimeFiles: {
+ "/node_modules/foo/index.js": /* js */ `
+ export const foo = 'foo'
+ export const bar = 'bar'
+ `,
+ "/test.js": /* js */ `
+ import * as mod from './out.js'
+ console.log(JSON.stringify(mod))
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: '{"bar":"bar","foo":"foo"}',
+ },
+ });
+ itBundled("importstar/ReExportStarExternalCommonJS", {
+ files: {
+ "/entry.js": `export * from "foo"`,
+ },
+ external: ["foo"],
+ format: "cjs",
+ runtimeFiles: {
+ "/node_modules/foo/index.js": /* js */ `
+ module.exports = { bar: 'bar', foo: 'foo' }
+ `,
+ "/test.js": /* js */ `
+ console.log(JSON.stringify(require('./out.js')))
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: '{"bar":"bar","foo":"foo"}',
+ },
+ });
+ itBundled("importstar/ImportDefaultNamespaceComboIssue446", {
+ files: {
+ "/external-default2.js": /* js */ `
+ import def, {default as default2} from 'external'
+ console.log(def, default2)
+ `,
+ "/external-ns.js": /* js */ `
+ import def, * as ns from 'external'
+ console.log(def, ns)
+ `,
+ "/external-ns-default.js": /* js */ `
+ import def, * as ns from 'external'
+ console.log(def, ns, ns.default)
+ `,
+ "/external-ns-def.js": /* js */ `
+ import def, * as ns from 'external'
+ console.log(def, ns, ns.def)
+ `,
+ "/external-default.js": /* js */ `
+ import def, * as ns from 'external'
+ console.log(def, ns.default)
+ `,
+ "/external-def.js": /* js */ `
+ import def, * as ns from 'external'
+ console.log(def, ns.def)
+ `,
+ "/internal-default2.js": /* js */ `
+ import def, {default as default2} from './internal'
+ console.log(def, default2)
+ `,
+ "/internal-ns.js": /* js */ `
+ import def, * as ns from './internal'
+ console.log(def, ns)
+ `,
+ "/internal-ns-default.js": /* js */ `
+ import def, * as ns from './internal'
+ console.log(def, ns, ns.default)
+ `,
+ "/internal-ns-def.js": /* js */ `
+ import def, * as ns from './internal'
+ console.log(def, ns, ns.def)
+ `,
+ "/internal-default.js": /* js */ `
+ import def, * as ns from './internal'
+ console.log(def, ns.default)
+ `,
+ "/internal-def.js": /* js */ `
+ import def, * as ns from './internal'
+ console.log(def, ns.def)
+ `,
+ "/internal.js": `export default 123`,
+
+ "/test.js": /* js */ `
+ import "./external-default2.js";
+ import "./external-default.js";
+ import "./external-def.js";
+ import "./external-ns-default.js";
+ import "./external-ns-def.js";
+ import "./external-ns.js";
+ import "./internal-default2.js";
+ import "./internal-default.js";
+ import "./internal-def.js";
+ import "./internal-ns-default.js";
+ import "./internal-ns-def.js";
+ import "./internal-ns.js";
+ `,
+ },
+ entryPoints: [
+ "/external-default2.js",
+ "/external-ns.js",
+ "/external-ns-default.js",
+ "/external-ns-def.js",
+ "/external-default.js",
+ "/external-def.js",
+ "/internal-default2.js",
+ "/internal-ns.js",
+ "/internal-ns-default.js",
+ "/internal-ns-def.js",
+ "/internal-default.js",
+ "/internal-def.js",
+ ],
+ external: ["external"],
+ run: {
+ file: "/test.js",
+ stdout: `
+ [Function: child] [Function: child]
+ [Function: child] [Function: child]
+ [Function: child] undefined
+ [Function: child] [Function: child] [Function: child]
+ [Function: child] [Function: child] undefined
+ [Function: child] [Function: child]
+ 123 123
+ 123 123
+ 123 undefined
+ 123 Module {
+ "default": 123
+ } 123
+ 123 Module {
+ "default": 123
+ } undefined
+ 123 Module {
+ "default": 123
+ }
+ `,
+ },
+ });
+ itBundled("importstar/ImportDefaultNamespaceComboNoDefault", {
+ files: {
+ "/entry-default-ns-prop.js": `import def, * as ns from './foo'; console.log(def, ns, ns.default)`,
+ "/entry-default-ns.js": `import def, * as ns from './foo'; console.log(def, ns)`,
+ "/entry-default-prop.js": `import def, * as ns from './foo'; console.log(def, ns.default)`,
+ "/entry-default.js": `import def from './foo'; console.log(def)`,
+ "/entry-prop.js": `import * as ns from './foo'; console.log(ns.default)`,
+ "/foo.js": `export let foo = 123`,
+ },
+ entryPoints: [
+ "/entry-default-ns-prop.js",
+ "/entry-default-ns.js",
+ "/entry-default-prop.js",
+ "/entry-default.js",
+ "/entry-prop.js",
+ ],
+ bundleErrors: {
+ "/entry-default-ns-prop.js": ['No matching export in "foo.js" for import "default"'],
+ "/entry-default-ns.js": ['No matching export in "foo.js" for import "default"'],
+ "/entry-default-prop.js": ['No matching export in "foo.js" for import "default"'],
+ "/entry-default.js": ['No matching export in "foo.js" for import "default"'],
+ },
+ });
+ itBundled("importstar/ImportNamespaceUndefinedPropertyEmptyFile", {
+ files: {
+ "/entry-nope.js": /* js */ `
+ import * as js from './empty.js'
+ import * as mjs from './empty.mjs'
+ import * as cjs from './empty.cjs'
+ console.log(
+ js.nope,
+ mjs.nope,
+ cjs.nope,
+ )
+ `,
+ "/entry-default.js": /* js */ `
+ import * as js from './empty.js'
+ import * as mjs from './empty.mjs'
+ import * as cjs from './empty.cjs'
+ console.log(
+ js.default,
+ mjs.default,
+ cjs.default,
+ )
+ `,
+ "/empty.js": ``,
+ "/empty.mjs": ``,
+ "/empty.cjs": ``,
+ },
+ entryPoints: ["/entry-nope.js", "/entry-default.js"],
+ bundleWarnings: {
+ "/entry-nope.js": [
+ 'WARNING: Import "nope" will always be undefined because the file "empty.js" has no exports',
+ 'WARNING: Import "nope" will always be undefined because the file "empty.mjs" has no exports',
+ 'WARNING: Import "nope" will always be undefined because the file "empty.cjs" has no exports',
+ ],
+ },
+ run: [
+ {
+ file: "/out/entry-nope.js",
+ stdout: `undefined undefined undefined`,
+ },
+ {
+ file: "/out/entry-default.js",
+ stdout: `{} undefined {}`,
+ },
+ ],
+ });
+ itBundled("importstar/ImportNamespaceUndefinedPropertySideEffectFreeFile", {
+ files: {
+ "/entry-nope.js": /* js */ `
+ import * as js from './foo/no-side-effects.js'
+ import * as mjs from './foo/no-side-effects.mjs'
+ import * as cjs from './foo/no-side-effects.cjs'
+ console.log(
+ js.nope,
+ mjs.nope,
+ cjs.nope,
+ )
+ `,
+ "/entry-default.js": /* js */ `
+ import * as js from './foo/no-side-effects.js'
+ import * as mjs from './foo/no-side-effects.mjs'
+ import * as cjs from './foo/no-side-effects.cjs'
+ console.log(
+ js.default,
+ mjs.default,
+ cjs.default,
+ )
+ `,
+ "/foo/package.json": `{ "sideEffects": false }`,
+ "/foo/no-side-effects.js": `console.log('js')`,
+ "/foo/no-side-effects.mjs": `console.log('mjs')`,
+ "/foo/no-side-effects.cjs": `console.log('cjs')`,
+ },
+ entryPoints: ["/entry-nope.js", "/entry-default.js"],
+ run: [
+ {
+ file: "/out/entry-nope.js",
+ stdout: `js\ncjs\nundefined undefined undefined`,
+ },
+ {
+ file: "/out/entry-default.js",
+ stdout: `js\ncjs\n{} undefined {}`,
+ },
+ ],
+ });
+ itBundled("importstar/ReExportStarEntryPointAndInnerFile", {
+ files: {
+ "/entry.js": /* js */ `
+ export * from 'a'
+ import * as inner from './inner.js'
+ export { inner }
+ `,
+ "/inner.js": `export * from 'b'`,
+ },
+ format: "cjs",
+ external: ["a", "b"],
+ runtimeFiles: {
+ "/node_modules/a/index.js": /* js */ `
+ export const a = 123;
+ `,
+ "/node_modules/b/index.js": /* js */ `
+ export const b = 456;
+ `,
+
+ "/test.js": /* js */ `
+ console.log(JSON.stringify(require('./out.js')))
+ `,
+ },
+ run: {
+ file: "/test.js",
+ stdout: '{"inner":{"b":456},"a":123}',
+ },
+ });
+});
diff --git a/test/bundler/esbuild/importstar_ts.test.ts b/test/bundler/esbuild/importstar_ts.test.ts
new file mode 100644
index 000000000..5641753c0
--- /dev/null
+++ b/test/bundler/esbuild/importstar_ts.test.ts
@@ -0,0 +1,305 @@
+import { test, describe } from "bun:test";
+import { expectBundled, itBundled } from "../expectBundled";
+
+// Tests ported from:
+// https://github.com/evanw/esbuild/blob/main/internal/bundler_tests/bundler_importstar_ts_test.go
+
+// For debug, all files are written to $TEMP/bun-bundle-tests/ts
+
+describe("bundler", () => {
+ return;
+ itBundled("ts/TSImportStarUnused", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import * as ns from './foo'
+ let foo = 234
+ console.log(foo)
+ `,
+ "/foo.ts": `export const foo = 123`,
+ },
+ });
+ itBundled("ts/TSImportStarCapture", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import * as ns from './foo'
+ let foo = 234
+ console.log(ns, ns.foo, foo)
+ `,
+ "/foo.ts": `export const foo = 123`,
+ },
+ });
+ itBundled("ts/TSImportStarNoCapture", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import * as ns from './foo'
+ let foo = 234
+ console.log(ns.foo, ns.foo, foo)
+ `,
+ "/foo.ts": `export const foo = 123`,
+ },
+ });
+ itBundled("ts/TSImportStarExportImportStarUnused", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import {ns} from './bar'
+ let foo = 234
+ console.log(foo)
+ `,
+ "/foo.ts": `export const foo = 123`,
+ "/bar.ts": /* ts */ `
+ import * as ns from './foo'
+ export {ns}
+ `,
+ },
+ });
+ itBundled("ts/TSImportStarExportImportStarNoCapture", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import {ns} from './bar'
+ let foo = 234
+ console.log(ns.foo, ns.foo, foo)
+ `,
+ "/foo.ts": `export const foo = 123`,
+ "/bar.ts": /* ts */ `
+ import * as ns from './foo'
+ export {ns}
+ `,
+ },
+ });
+ itBundled("ts/TSImportStarExportImportStarCapture", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import {ns} from './bar'
+ let foo = 234
+ console.log(ns, ns.foo, foo)
+ `,
+ "/foo.ts": `export const foo = 123`,
+ "/bar.ts": /* ts */ `
+ import * as ns from './foo'
+ export {ns}
+ `,
+ },
+ });
+ itBundled("ts/TSImportStarExportStarAsUnused", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import {ns} from './bar'
+ let foo = 234
+ console.log(foo)
+ `,
+ "/foo.ts": `export const foo = 123`,
+ "/bar.ts": `export * as ns from './foo'`,
+ },
+ });
+ itBundled("ts/TSImportStarExportStarAsNoCapture", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import {ns} from './bar'
+ let foo = 234
+ console.log(ns.foo, ns.foo, foo)
+ `,
+ "/foo.ts": `export const foo = 123`,
+ "/bar.ts": `export * as ns from './foo'`,
+ },
+ });
+ itBundled("ts/TSImportStarExportStarAsCapture", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import {ns} from './bar'
+ let foo = 234
+ console.log(ns, ns.foo, foo)
+ `,
+ "/foo.ts": `export const foo = 123`,
+ "/bar.ts": `export * as ns from './foo'`,
+ },
+ });
+ itBundled("ts/TSImportStarExportStarUnused", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import * as ns from './bar'
+ let foo = 234
+ console.log(foo)
+ `,
+ "/foo.ts": `export const foo = 123`,
+ "/bar.ts": `export * from './foo'`,
+ },
+ });
+ itBundled("ts/TSImportStarExportStarNoCapture", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import * as ns from './bar'
+ let foo = 234
+ console.log(ns.foo, ns.foo, foo)
+ `,
+ "/foo.ts": `export const foo = 123`,
+ "/bar.ts": `export * from './foo'`,
+ },
+ });
+ itBundled("ts/TSImportStarExportStarCapture", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import * as ns from './bar'
+ let foo = 234
+ console.log(ns, ns.foo, foo)
+ `,
+ "/foo.ts": `export const foo = 123`,
+ "/bar.ts": `export * from './foo'`,
+ },
+ });
+ itBundled("ts/TSImportStarCommonJSUnused", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import * as ns from './foo'
+ let foo = 234
+ console.log(foo)
+ `,
+ "/foo.ts": `exports.foo = 123`,
+ },
+ });
+ itBundled("ts/TSImportStarCommonJSCapture", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import * as ns from './foo'
+ let foo = 234
+ console.log(ns, ns.foo, foo)
+ `,
+ "/foo.ts": `exports.foo = 123`,
+ },
+ });
+ itBundled("ts/TSImportStarCommonJSNoCapture", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import * as ns from './foo'
+ let foo = 234
+ console.log(ns.foo, ns.foo, foo)
+ `,
+ "/foo.ts": `exports.foo = 123`,
+ },
+ });
+ itBundled("ts/TSImportStarAndCommonJS", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './foo'
+ const ns2 = require('./foo')
+ console.log(ns.foo, ns2.foo)
+ `,
+ "/foo.ts": `export const foo = 123`,
+ },
+ });
+ itBundled("ts/TSImportStarNoBundleUnused", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import * as ns from './foo'
+ let foo = 234
+ console.log(foo)
+ `,
+ },
+ mode: "transform",
+ });
+ itBundled("ts/TSImportStarNoBundleCapture", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import * as ns from './foo'
+ let foo = 234
+ console.log(ns, ns.foo, foo)
+ `,
+ },
+ mode: "transform",
+ });
+ itBundled("ts/TSImportStarNoBundleNoCapture", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import * as ns from './foo'
+ let foo = 234
+ console.log(ns.foo, ns.foo, foo)
+ `,
+ },
+ mode: "transform",
+ });
+ itBundled("ts/TSImportStarMangleNoBundleUnused", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import * as ns from './foo'
+ let foo = 234
+ console.log(foo)
+ `,
+ },
+ minifySyntax: true,
+ mode: "transform",
+ });
+ itBundled("ts/TSImportStarMangleNoBundleCapture", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import * as ns from './foo'
+ let foo = 234
+ console.log(ns, ns.foo, foo)
+ `,
+ },
+ minifySyntax: true,
+ mode: "transform",
+ });
+ itBundled("ts/TSImportStarMangleNoBundleNoCapture", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import * as ns from './foo'
+ let foo = 234
+ console.log(ns.foo, ns.foo, foo)
+ `,
+ },
+ minifySyntax: true,
+ mode: "transform",
+ });
+ itBundled("ts/TSReExportTypeOnlyFileES6", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import * as ns from './re-export'
+ console.log(ns.foo)
+ `,
+ "/re-export.ts": /* ts */ `
+ export * from './types1'
+ export * from './types2'
+ export * from './types3'
+ export * from './values'
+ `,
+ "/types1.ts": /* ts */ `
+ export interface Foo {}
+ export type Bar = number
+ console.log('some code')
+ `,
+ "/types2.ts": /* ts */ `
+ import {Foo} from "./type"
+ export {Foo}
+ console.log('some code')
+ `,
+ "/types3.ts": /* ts */ `
+ export {Foo} from "./type"
+ console.log('some code')
+ `,
+ "/values.ts": `export let foo = 123`,
+ "/type.ts": `export type Foo = number`,
+ },
+ });
+});
diff --git a/test/bundler/esbuild/loader.test.ts b/test/bundler/esbuild/loader.test.ts
new file mode 100644
index 000000000..eb8e38f92
--- /dev/null
+++ b/test/bundler/esbuild/loader.test.ts
@@ -0,0 +1,771 @@
+import { expectBundled, itBundled, testForFile } from "../expectBundled";
+var { describe, test, expect } = testForFile(import.meta.path);
+
+// Tests ported from:
+// https://github.com/evanw/esbuild/blob/main/internal/bundler_tests/bundler_loader_test.go
+
+// For debug, all files are written to $TEMP/bun-bundle-tests/loader
+
+describe("bundler", () => {
+ return;
+ itBundled("loader/LoaderFile", {
+ // GENERATED
+ files: {
+ "/entry.js": `console.log(require('./test.svg'))`,
+ "/test.svg": `<svg></svg>`,
+ },
+ outdir: "/out/",
+ });
+ itBundled("loader/LoaderFileMultipleNoCollision", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ console.log(
+ require('./a/test.txt'),
+ require('./b/test.txt'),
+ )
+ `,
+ "/a/test.txt": `test`,
+ "/b/test.txt": `test`,
+ },
+ outfile: "/dist/out.js",
+ });
+ itBundled("loader/JSXSyntaxInJSWithJSXLoader", {
+ // GENERATED
+ files: {
+ "/entry.js": `console.log(<div/>)`,
+ },
+ });
+ itBundled("loader/JSXPreserveCapitalLetter", {
+ // GENERATED
+ files: {
+ "/entry.jsx": /* jsx */ `
+ import { mustStartWithUpperCaseLetter as Test } from './foo'
+ console.log(<Test/>)
+ `,
+ "/foo.js": `export class mustStartWithUpperCaseLetter {}`,
+ },
+ });
+ itBundled("loader/JSXPreserveCapitalLetterMinify", {
+ // GENERATED
+ files: {
+ "/entry.jsx": /* jsx */ `
+ import { mustStartWithUpperCaseLetter as XYYYY } from './foo'
+ console.log(<XYYYY tag-must-start-with-capital-letter />)
+ `,
+ "/foo.js": `export class mustStartWithUpperCaseLetter {}`,
+ },
+ minifyIdentifiers: true,
+ });
+ itBundled("loader/JSXPreserveCapitalLetterMinifyNested", {
+ // GENERATED
+ files: {
+ "/entry.jsx": /* jsx */ `
+ x = () => {
+ class XYYYYY {} // This should be named "Y" due to frequency analysis
+ return <XYYYYY tag-must-start-with-capital-letter />
+ }
+ `,
+ },
+ minifyIdentifiers: true,
+ });
+ itBundled("loader/RequireCustomExtensionString", {
+ // GENERATED
+ files: {
+ "/entry.js": `console.log(require('./test.custom'))`,
+ "/test.custom": `#include <stdio.h>`,
+ },
+ });
+ itBundled("loader/RequireCustomExtensionBase64", {
+ // GENERATED
+ files: {
+ "/entry.js": `console.log(require('./test.custom'))`,
+ "/test.custom": `a\x00b\x80c\xFFd`,
+ },
+ });
+ itBundled("loader/RequireCustomExtensionDataURL", {
+ // GENERATED
+ files: {
+ "/entry.js": `console.log(require('./test.custom'))`,
+ "/test.custom": `a\x00b\x80c\xFFd`,
+ },
+ });
+ itBundled("loader/RequireCustomExtensionPreferLongest", {
+ // GENERATED
+ files: {
+ "/entry.js": `console.log(require('./test.txt'), require('./test.base64.txt'))`,
+ "/test.txt": `test.txt`,
+ "/test.base64.txt": `test.base64.txt`,
+ },
+ });
+ itBundled("loader/AutoDetectMimeTypeFromExtension", {
+ // GENERATED
+ files: {
+ "/entry.js": `console.log(require('./test.svg'))`,
+ "/test.svg": `a\x00b\x80c\xFFd`,
+ },
+ });
+ itBundled("loader/LoaderJSONCommonJSAndES6", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ const x_json = require('./x.json')
+ import y_json from './y.json'
+ import {small, if as fi} from './z.json'
+ console.log(x_json, y_json, small, fi)
+ `,
+ "/x.json": `{"x": true}`,
+ "/y.json": `{"y1": true, "y2": false}`,
+ "/z.json": /* json */ `
+ {
+ "big": "this is a big long line of text that should be discarded",
+ "small": "some small text",
+ "if": "test keyword imports"
+ }
+ `,
+ },
+ });
+ itBundled("loader/LoaderJSONInvalidIdentifierES6", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './test.json'
+ import * as ns2 from './test2.json'
+ console.log(ns['invalid-identifier'], ns2)
+ `,
+ "/test.json": `{"invalid-identifier": true}`,
+ "/test2.json": `{"invalid-identifier": true}`,
+ },
+ });
+ itBundled("loader/LoaderJSONMissingES6", {
+ // GENERATED
+ files: {
+ "/entry.js": `import {missing} from './test.json'`,
+ "/test.json": `{"present": true}`,
+ },
+ /* TODO FIX expectedCompileLog: `entry.js: ERROR: No matching export in "test.json" for import "missing"
+ `, */
+ });
+ itBundled("loader/LoaderTextCommonJSAndES6", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ const x_txt = require('./x.txt')
+ import y_txt from './y.txt'
+ console.log(x_txt, y_txt)
+ `,
+ "/x.txt": `x`,
+ "/y.txt": `y`,
+ },
+ });
+ itBundled("loader/LoaderBase64CommonJSAndES6", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ const x_b64 = require('./x.b64')
+ import y_b64 from './y.b64'
+ console.log(x_b64, y_b64)
+ `,
+ "/x.b64": `x`,
+ "/y.b64": `y`,
+ },
+ });
+ itBundled("loader/LoaderDataURLCommonJSAndES6", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ const x_url = require('./x.txt')
+ import y_url from './y.txt'
+ console.log(x_url, y_url)
+ `,
+ "/x.txt": `x`,
+ "/y.txt": `y`,
+ },
+ });
+ itBundled("loader/LoaderFileCommonJSAndES6", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ const x_url = require('./x.txt')
+ import y_url from './y.txt'
+ console.log(x_url, y_url)
+ `,
+ "/x.txt": `x`,
+ "/y.txt": `y`,
+ },
+ });
+ itBundled("loader/LoaderFileRelativePathJS", {
+ // GENERATED
+ files: {
+ "/src/entries/entry.js": /* js */ `
+ import x from '../images/image.png'
+ console.log(x)
+ `,
+ "/src/images/image.png": `x`,
+ },
+ outbase: "/src",
+ });
+ itBundled("loader/LoaderFileRelativePathCSS", {
+ // GENERATED
+ files: {
+ "/src/entries/entry.css": /* css */ `
+ div {
+ background: url(../images/image.png);
+ }
+ `,
+ "/src/images/image.png": `x`,
+ },
+ outbase: "/src",
+ });
+ itBundled("loader/LoaderFileRelativePathAssetNamesJS", {
+ // GENERATED
+ files: {
+ "/src/entries/entry.js": /* js */ `
+ import x from '../images/image.png'
+ console.log(x)
+ `,
+ "/src/images/image.png": `x`,
+ },
+ outbase: "/src",
+ assetNames: "[dir]/[name]-[hash]",
+ });
+ itBundled("loader/LoaderFileExtPathAssetNamesJS", {
+ // GENERATED
+ files: {
+ "/src/entries/entry.js": /* js */ `
+ import x from '../images/image.png'
+ import y from '../uploads/file.txt'
+ console.log(x, y)
+ `,
+ "/src/images/image.png": `x`,
+ "/src/uploads/file.txt": `y`,
+ },
+ outbase: "/src",
+ assetNames: "[ext]/[name]-[hash]",
+ });
+ itBundled("loader/LoaderFileRelativePathAssetNamesCSS", {
+ // GENERATED
+ files: {
+ "/src/entries/entry.css": /* css */ `
+ div {
+ background: url(../images/image.png);
+ }
+ `,
+ "/src/images/image.png": `x`,
+ },
+ outbase: "/src",
+ assetNames: "[dir]/[name]-[hash]",
+ });
+ itBundled("loader/LoaderFilePublicPathJS", {
+ // GENERATED
+ files: {
+ "/src/entries/entry.js": /* js */ `
+ import x from '../images/image.png'
+ console.log(x)
+ `,
+ "/src/images/image.png": `x`,
+ },
+ outbase: "/src",
+ publicPath: "https://example.com",
+ });
+ itBundled("loader/LoaderFilePublicPathCSS", {
+ // GENERATED
+ files: {
+ "/src/entries/entry.css": /* css */ `
+ div {
+ background: url(../images/image.png);
+ }
+ `,
+ "/src/images/image.png": `x`,
+ },
+ outbase: "/src",
+ publicPath: "https://example.com",
+ });
+ itBundled("loader/LoaderFilePublicPathAssetNamesJS", {
+ // GENERATED
+ files: {
+ "/src/entries/entry.js": /* js */ `
+ import x from '../images/image.png'
+ console.log(x)
+ `,
+ "/src/images/image.png": `x`,
+ },
+ outbase: "/src",
+ publicPath: "https://example.com",
+ assetNames: "[dir]/[name]-[hash]",
+ });
+ itBundled("loader/LoaderFilePublicPathAssetNamesCSS", {
+ // GENERATED
+ files: {
+ "/src/entries/entry.css": /* css */ `
+ div {
+ background: url(../images/image.png);
+ }
+ `,
+ "/src/images/image.png": `x`,
+ },
+ outbase: "/src",
+ publicPath: "https://example.com",
+ assetNames: "[dir]/[name]-[hash]",
+ });
+ itBundled("loader/LoaderFileOneSourceTwoDifferentOutputPathsJS", {
+ // GENERATED
+ files: {
+ "/src/entries/entry.js": `import '../shared/common.js'`,
+ "/src/entries/other/entry.js": `import '../../shared/common.js'`,
+ "/src/shared/common.js": /* js */ `
+ import x from './common.png'
+ console.log(x)
+ `,
+ "/src/shared/common.png": `x`,
+ },
+ entryPoints: ["/src/entries/entry.js", "/src/entries/other/entry.js"],
+ outbase: "/src",
+ });
+ itBundled("loader/LoaderFileOneSourceTwoDifferentOutputPathsCSS", {
+ // GENERATED
+ files: {
+ "/src/entries/entry.css": `@import "../shared/common.css";`,
+ "/src/entries/other/entry.css": `@import "../../shared/common.css";`,
+ "/src/shared/common.css": /* css */ `
+ div {
+ background: url(common.png);
+ }
+ `,
+ "/src/shared/common.png": `x`,
+ },
+ entryPoints: ["/src/entries/entry.css", "/src/entries/other/entry.css"],
+ outbase: "/src",
+ });
+ itBundled("loader/LoaderJSONNoBundle", {
+ // GENERATED
+ files: {
+ "/test.json": `{"test": 123, "invalid-identifier": true}`,
+ },
+ mode: "transform",
+ });
+ itBundled("loader/LoaderJSONNoBundleES6", {
+ // GENERATED
+ files: {
+ "/test.json": `{"test": 123, "invalid-identifier": true}`,
+ },
+ format: "esm",
+ unsupportedJSFeatures: "ArbitraryModuleNamespaceNames",
+ mode: "convertformat",
+ });
+ itBundled("loader/LoaderJSONNoBundleES6ArbitraryModuleNamespaceNames", {
+ // GENERATED
+ files: {
+ "/test.json": `{"test": 123, "invalid-identifier": true}`,
+ },
+ format: "esm",
+ mode: "convertformat",
+ });
+ itBundled("loader/LoaderJSONNoBundleCommonJS", {
+ // GENERATED
+ files: {
+ "/test.json": `{"test": 123, "invalid-identifier": true}`,
+ },
+ format: "cjs",
+ mode: "convertformat",
+ });
+ itBundled("loader/LoaderJSONNoBundleIIFE", {
+ // GENERATED
+ files: {
+ "/test.json": `{"test": 123, "invalid-identifier": true}`,
+ },
+ format: "iife",
+ mode: "convertformat",
+ });
+ itBundled("loader/LoaderJSONSharedWithMultipleEntriesIssue413", {
+ // GENERATED
+ files: {
+ "/a.js": /* js */ `
+ import data from './data.json'
+ console.log('a:', data)
+ `,
+ "/b.js": /* js */ `
+ import data from './data.json'
+ console.log('b:', data)
+ `,
+ "/data.json": `{"test": 123}`,
+ },
+ entryPoints: ["/a.js", "/b.js"],
+ format: "esm",
+ });
+ itBundled("loader/LoaderFileWithQueryParameter", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ // Each of these should have a separate identity (i.e. end up in the output file twice)
+ import foo from './file.txt?foo'
+ import bar from './file.txt?bar'
+ console.log(foo, bar)
+ `,
+ "/file.txt": `This is some text`,
+ },
+ });
+ itBundled("loader/LoaderFromExtensionWithQueryParameter", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import foo from './file.abc?query.xyz'
+ console.log(foo)
+ `,
+ "/file.abc": `This should not be base64 encoded`,
+ },
+ });
+ itBundled("loader/LoaderDataURLTextCSS", {
+ // GENERATED
+ files: {
+ "/entry.css": /* css */ `
+ @import "data:text/css,body{color:%72%65%64}";
+ @import "data:text/css;base64,Ym9keXtiYWNrZ3JvdW5kOmJsdWV9";
+ @import "data:text/css;charset=UTF-8,body{color:%72%65%64}";
+ @import "data:text/css;charset=UTF-8;base64,Ym9keXtiYWNrZ3JvdW5kOmJsdWV9";
+ `,
+ },
+ });
+ itBundled("loader/LoaderDataURLTextCSSCannotImport", {
+ // GENERATED
+ files: {
+ "/entry.css": `@import "data:text/css,@import './other.css';";`,
+ "/other.css": `div { should-not-be-imported: true }`,
+ },
+ /* TODO FIX expectedScanLog: `<data:text/css,@import './other.css';>: ERROR: Could not resolve "./other.css"
+ `, */
+ });
+ itBundled("loader/LoaderDataURLTextJavaScript", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import "data:text/javascript,console.log('%31%32%33')";
+ import "data:text/javascript;base64,Y29uc29sZS5sb2coMjM0KQ==";
+ import "data:text/javascript;charset=UTF-8,console.log(%31%32%33)";
+ import "data:text/javascript;charset=UTF-8;base64,Y29uc29sZS5sb2coMjM0KQ==";
+ `,
+ },
+ });
+ itBundled("loader/LoaderDataURLTextJavaScriptCannotImport", {
+ // GENERATED
+ files: {
+ "/entry.js": `import "data:text/javascript,import './other.js'"`,
+ "/other.js": `shouldNotBeImported = true`,
+ },
+ /* TODO FIX expectedScanLog: `<data:text/javascript,import './other.js'>: ERROR: Could not resolve "./other.js"
+ `, */
+ });
+ itBundled("loader/LoaderDataURLTextJavaScriptPlusCharacter", {
+ // GENERATED
+ files: {
+ "/entry.js": `import "data:text/javascript,console.log(1+2)";`,
+ },
+ });
+ itBundled("loader/LoaderDataURLApplicationJSON", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import a from 'data:application/json,"%31%32%33"';
+ import b from 'data:application/json;base64,eyJ3b3JrcyI6dHJ1ZX0=';
+ import c from 'data:application/json;charset=UTF-8,%31%32%33';
+ import d from 'data:application/json;charset=UTF-8;base64,eyJ3b3JrcyI6dHJ1ZX0=';
+ console.log([
+ a, b, c, d,
+ ])
+ `,
+ },
+ });
+ itBundled("loader/LoaderDataURLUnknownMIME", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import a from 'data:some/thing;what,someData%31%32%33';
+ import b from 'data:other/thing;stuff;base64,c29tZURhdGEyMzQ=';
+ console.log(a, b)
+ `,
+ },
+ });
+ itBundled("loader/LoaderDataURLExtensionBasedMIME", {
+ // GENERATED
+ files: {
+ "/entry.foo": /* foo */ `
+ export { default as css } from "./example.css"
+ export { default as eot } from "./example.eot"
+ export { default as gif } from "./example.gif"
+ export { default as htm } from "./example.htm"
+ export { default as html } from "./example.html"
+ export { default as jpeg } from "./example.jpeg"
+ export { default as jpg } from "./example.jpg"
+ export { default as js } from "./example.js"
+ export { default as json } from "./example.json"
+ export { default as mjs } from "./example.mjs"
+ export { default as otf } from "./example.otf"
+ export { default as pdf } from "./example.pdf"
+ export { default as png } from "./example.png"
+ export { default as sfnt } from "./example.sfnt"
+ export { default as svg } from "./example.svg"
+ export { default as ttf } from "./example.ttf"
+ export { default as wasm } from "./example.wasm"
+ export { default as webp } from "./example.webp"
+ export { default as woff } from "./example.woff"
+ export { default as woff2 } from "./example.woff2"
+ export { default as xml } from "./example.xml"
+ `,
+ "/example.css": `css`,
+ "/example.eot": `eot`,
+ "/example.gif": `gif`,
+ "/example.htm": `htm`,
+ "/example.html": `html`,
+ "/example.jpeg": `jpeg`,
+ "/example.jpg": `jpg`,
+ "/example.js": `js`,
+ "/example.json": `json`,
+ "/example.mjs": `mjs`,
+ "/example.otf": `otf`,
+ "/example.pdf": `pdf`,
+ "/example.png": `png`,
+ "/example.sfnt": `sfnt`,
+ "/example.svg": `svg`,
+ "/example.ttf": `ttf`,
+ "/example.wasm": `wasm`,
+ "/example.webp": `webp`,
+ "/example.woff": `woff`,
+ "/example.woff2": `woff2`,
+ "/example.xml": `xml`,
+ },
+ });
+ itBundled("loader/LoaderDataURLBase64VsPercentEncoding", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import a from './shouldUsePercent_1.txt'
+ import b from './shouldUsePercent_2.txt'
+ import c from './shouldUseBase64_1.txt'
+ import d from './shouldUseBase64_2.txt'
+ console.log(
+ a,
+ b,
+ c,
+ d,
+ )
+ `,
+ "/shouldUsePercent_1.txt": `\n\n\n`,
+ "/shouldUsePercent_2.txt": `\n\n\n\n`,
+ "/shouldUseBase64_1.txt": `\n\n\n\n\n`,
+ "/shouldUseBase64_2.txt": `\n\n\n\n\n\n`,
+ },
+ });
+ itBundled("loader/LoaderDataURLBase64InvalidUTF8", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import a from './binary.txt'
+ console.log(a)
+ `,
+ "/binary.txt": `\xFF`,
+ },
+ });
+ itBundled("loader/LoaderDataURLEscapePercents", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import a from './percents.txt'
+ console.log(a)
+ `,
+ "/percents.txt": /* txt */ `
+ %, %3, %33, %333
+ %, %e, %ee, %eee
+ %, %E, %EE, %EEE
+ `,
+ },
+ });
+ itBundled("loader/LoaderCopyWithBundleFromJS", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import x from "../assets/some.file"
+ console.log(x)
+ `,
+ "/Users/user/project/assets/some.file": `stuff`,
+ },
+ outbase: "/Users/user/project",
+ });
+ itBundled("loader/LoaderCopyWithBundleFromCSS", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.css": /* css */ `
+ body {
+ background: url(../assets/some.file);
+ }
+ `,
+ "/Users/user/project/assets/some.file": `stuff`,
+ },
+ outbase: "/Users/user/project",
+ });
+ itBundled("loader/LoaderCopyWithBundleEntryPoint", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import x from "../assets/some.file"
+ console.log(x)
+ `,
+ "/Users/user/project/src/entry.css": /* css */ `
+ body {
+ background: url(../assets/some.file);
+ }
+ `,
+ "/Users/user/project/assets/some.file": `stuff`,
+ },
+ entryPoints: [
+ "/Users/user/project/src/entry.js",
+ "/Users/user/project/src/entry.css",
+ "/Users/user/project/assets/some.file",
+ ],
+ outbase: "/Users/user/project",
+ });
+ itBundled("loader/LoaderCopyWithTransform", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": `console.log('entry')`,
+ "/Users/user/project/assets/some.file": `stuff`,
+ },
+ entryPoints: ["/Users/user/project/src/entry.js", "/Users/user/project/assets/some.file"],
+ outbase: "/Users/user/project",
+ mode: "passthrough",
+ });
+ itBundled("loader/LoaderCopyWithFormat", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": `console.log('entry')`,
+ "/Users/user/project/assets/some.file": `stuff`,
+ },
+ entryPoints: ["/Users/user/project/src/entry.js", "/Users/user/project/assets/some.file"],
+ format: "iife",
+ outbase: "/Users/user/project",
+ mode: "convertformat",
+ });
+ itBundled("loader/JSXAutomaticNoNameCollision", {
+ // GENERATED
+ files: {
+ "/entry.jsx": /* jsx */ `
+ import { Link } from "@remix-run/react"
+ const x = <Link {...y} key={z} />
+ `,
+ },
+ format: "cjs",
+ mode: "convertformat",
+ });
+ itBundled("loader/AssertTypeJSONWrongLoader", {
+ // GENERATED
+ files: {
+ "/entry.js": `import foo from './foo.json' assert { type: 'json' }`,
+ "/foo.json": `{}`,
+ },
+ /* TODO FIX expectedScanLog: `entry.js: ERROR: The file "foo.json" was loaded with the "js" loader
+ entry.js: NOTE: This import assertion requires the loader to be "json" instead:
+ NOTE: You need to either reconfigure esbuild to ensure that the loader for this file is "json" or you need to remove this import assertion.
+ `, */
+ });
+ itBundled("loader/EmptyLoaderJS", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import './a.empty'
+ import * as ns from './b.empty'
+ import def from './c.empty'
+ import { named } from './d.empty'
+ console.log(ns, def, named)
+ `,
+ "/a.empty": `throw 'FAIL'`,
+ "/b.empty": `throw 'FAIL'`,
+ "/c.empty": `throw 'FAIL'`,
+ "/d.empty": `throw 'FAIL'`,
+ },
+ sourceMap: "external",
+ metafile: true,
+ /* TODO FIX expectedCompileLog: `entry.js: WARNING: Import "named" will always be undefined because the file "d.empty" has no exports
+ `, */
+ });
+ itBundled("loader/EmptyLoaderCSS", {
+ // GENERATED
+ files: {
+ "/entry.css": /* css */ `
+ @import 'a.empty';
+ a { background: url(b.empty) }
+ `,
+ "/a.empty": `body { color: fail }`,
+ "/b.empty": `fail`,
+ },
+ sourceMap: "external",
+ metafile: true,
+ });
+ itBundled("loader/ExtensionlessLoaderJS", {
+ // GENERATED
+ files: {
+ "/entry.js": `import './what'`,
+ "/what": `foo()`,
+ },
+ });
+ itBundled("loader/ExtensionlessLoaderCSS", {
+ // GENERATED
+ files: {
+ "/entry.css": `@import './what';`,
+ "/what": `.foo { color: red }`,
+ },
+ });
+ itBundled("loader/LoaderCopyEntryPointAdvanced", {
+ // GENERATED
+ files: {
+ "/project/entry.js": /* js */ `
+ import xyz from './xyz.copy'
+ console.log(xyz)
+ `,
+ "/project/TEST FAILED.copy": `some stuff`,
+ "/project/xyz.copy": `more stuff`,
+ },
+ /* TODO FIX entryPathsAdvanced: []bundler.EntryPoint{
+ {
+ InputPath: "/project/entry.js",
+ OutputPath: "js/input/path",
+ InputPathInFileNamespace: true,
+ },
+ {
+ InputPath: "/project/TEST FAILED.copy",
+ OutputPath: "copy/input/path",
+ InputPathInFileNamespace: true,
+ },
+ }, */
+ });
+ itBundled("loader/LoaderCopyUseIndex", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/index.copy": `some stuff`,
+ },
+ });
+ itBundled("loader/LoaderCopyExplicitOutputFile", {
+ // GENERATED
+ files: {
+ "/project/TEST FAILED.copy": `some stuff`,
+ },
+ outfile: "/out/this.worked",
+ });
+ itBundled("loader/LoaderCopyStartsWithDotAbsPath", {
+ // GENERATED
+ files: {
+ "/project/src/.htaccess": `some stuff`,
+ "/project/src/entry.js": `some.stuff()`,
+ "/project/src/.ts": `foo as number`,
+ },
+ entryPoints: ["/project/src/.htaccess", "/project/src/entry.js", "/project/src/.ts"],
+ });
+ itBundled("loader/LoaderCopyStartsWithDotRelPath", {
+ // GENERATED
+ files: {
+ "/project/src/.htaccess": `some stuff`,
+ "/project/src/entry.js": `some.stuff()`,
+ "/project/src/.ts": `foo as number`,
+ },
+ entryPoints: ["./.htaccess", "./entry.js", "./.ts"],
+ /* TODO FIX absWorkingDir: "/project/src", */
+ });
+});
diff --git a/test/bundler/esbuild/lower.test.ts b/test/bundler/esbuild/lower.test.ts
new file mode 100644
index 000000000..b22cdbd74
--- /dev/null
+++ b/test/bundler/esbuild/lower.test.ts
@@ -0,0 +1,1809 @@
+import { expectBundled, itBundled, testForFile } from "../expectBundled";
+var { describe, test, expect } = testForFile(import.meta.path);
+
+// Tests ported from:
+// https://github.com/evanw/esbuild/blob/main/internal/bundler_tests/bundler_lower_test.go
+
+// For debug, all files are written to $TEMP/bun-bundle-tests/lower
+
+describe("bundler", () => {
+ return;
+ itBundled("lower/LowerOptionalCatchNameCollisionNoBundle", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ try {}
+ catch { var e, e2 }
+ var e3
+ `,
+ },
+ unsupportedJSFeatures: "es2018",
+ mode: "transform",
+ });
+ itBundled("lower/LowerObjectSpreadNoBundle", {
+ // GENERATED
+ files: {
+ "/entry.jsx": /* jsx */ `
+ let tests = [
+ {...a, ...b},
+ {a, b, ...c},
+ {...a, b, c},
+ {a, ...b, c},
+ {a, b, ...c, ...d, e, f, ...g, ...h, i, j},
+ ]
+ let jsx = [
+ <div {...a} {...b}/>,
+ <div a b {...c}/>,
+ <div {...a} b c/>,
+ <div a {...b} c/>,
+ <div a b {...c} {...d} e f {...g} {...h} i j/>,
+ ]
+ `,
+ },
+ unsupportedJSFeatures: "es2017",
+ mode: "transform",
+ });
+ itBundled("lower/LowerExponentiationOperatorNoBundle", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ let tests = {
+ // Exponentiation operator
+ 0: a ** b ** c,
+ 1: (a ** b) ** c,
+
+ // Exponentiation assignment operator
+ 2: a **= b,
+ 3: a.b **= c,
+ 4: a[b] **= c,
+ 5: a().b **= c,
+ 6: a()[b] **= c,
+ 7: a[b()] **= c,
+ 8: a()[b()] **= c,
+
+ // These all should not need capturing (no object identity)
+ 9: a[0] **= b,
+ 10: a[false] **= b,
+ 11: a[null] **= b,
+ 12: a[void 0] **= b,
+ 13: a[123n] **= b,
+ 14: a[this] **= b,
+
+ // These should need capturing (have object identitiy)
+ 15: a[/x/] **= b,
+ 16: a[{}] **= b,
+ 17: a[[]] **= b,
+ 18: a[() => {}] **= b,
+ 19: a[function() {}] **= b,
+ }
+ `,
+ },
+ unsupportedJSFeatures: "es2015",
+ mode: "transform",
+ /* TODO FIX expectedScanLog: `entry.js: ERROR: Big integer literals are not available in the configured target environment
+ `, */
+ });
+ itBundled("lower/LowerPrivateFieldAssignments2015NoBundle", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Foo {
+ #x
+ unary() {
+ this.#x++
+ this.#x--
+ ++this.#x
+ --this.#x
+ }
+ binary() {
+ this.#x = 1
+ this.#x += 1
+ this.#x -= 1
+ this.#x *= 1
+ this.#x /= 1
+ this.#x %= 1
+ this.#x **= 1
+ this.#x <<= 1
+ this.#x >>= 1
+ this.#x >>>= 1
+ this.#x &= 1
+ this.#x |= 1
+ this.#x ^= 1
+ this.#x &&= 1
+ this.#x ||= 1
+ this.#x ??= 1
+ }
+ }
+ `,
+ },
+ unsupportedJSFeatures: "es2015",
+ mode: "transform",
+ });
+ itBundled("lower/LowerPrivateFieldAssignments2019NoBundle", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Foo {
+ #x
+ unary() {
+ this.#x++
+ this.#x--
+ ++this.#x
+ --this.#x
+ }
+ binary() {
+ this.#x = 1
+ this.#x += 1
+ this.#x -= 1
+ this.#x *= 1
+ this.#x /= 1
+ this.#x %= 1
+ this.#x **= 1
+ this.#x <<= 1
+ this.#x >>= 1
+ this.#x >>>= 1
+ this.#x &= 1
+ this.#x |= 1
+ this.#x ^= 1
+ this.#x &&= 1
+ this.#x ||= 1
+ this.#x ??= 1
+ }
+ }
+ `,
+ },
+ unsupportedJSFeatures: "es2019",
+ mode: "transform",
+ });
+ itBundled("lower/LowerPrivateFieldAssignments2020NoBundle", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Foo {
+ #x
+ unary() {
+ this.#x++
+ this.#x--
+ ++this.#x
+ --this.#x
+ }
+ binary() {
+ this.#x = 1
+ this.#x += 1
+ this.#x -= 1
+ this.#x *= 1
+ this.#x /= 1
+ this.#x %= 1
+ this.#x **= 1
+ this.#x <<= 1
+ this.#x >>= 1
+ this.#x >>>= 1
+ this.#x &= 1
+ this.#x |= 1
+ this.#x ^= 1
+ this.#x &&= 1
+ this.#x ||= 1
+ this.#x ??= 1
+ }
+ }
+ `,
+ },
+ unsupportedJSFeatures: "es2020",
+ mode: "transform",
+ });
+ itBundled("lower/LowerPrivateFieldAssignmentsNextNoBundle", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Foo {
+ #x
+ unary() {
+ this.#x++
+ this.#x--
+ ++this.#x
+ --this.#x
+ }
+ binary() {
+ this.#x = 1
+ this.#x += 1
+ this.#x -= 1
+ this.#x *= 1
+ this.#x /= 1
+ this.#x %= 1
+ this.#x **= 1
+ this.#x <<= 1
+ this.#x >>= 1
+ this.#x >>>= 1
+ this.#x &= 1
+ this.#x |= 1
+ this.#x ^= 1
+ this.#x &&= 1
+ this.#x ||= 1
+ this.#x ??= 1
+ }
+ }
+ `,
+ },
+ mode: "transform",
+ });
+ itBundled("lower/LowerPrivateFieldOptionalChain2019NoBundle", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Foo {
+ #x
+ foo() {
+ this?.#x.y
+ this?.y.#x
+ this.#x?.y
+ }
+ }
+ `,
+ },
+ unsupportedJSFeatures: "es2019",
+ mode: "transform",
+ });
+ itBundled("lower/LowerPrivateFieldOptionalChain2020NoBundle", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Foo {
+ #x
+ foo() {
+ this?.#x.y
+ this?.y.#x
+ this.#x?.y
+ }
+ }
+ `,
+ },
+ unsupportedJSFeatures: "es2020",
+ mode: "transform",
+ });
+ itBundled("lower/LowerPrivateFieldOptionalChainNextNoBundle", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Foo {
+ #x
+ foo() {
+ this?.#x.y
+ this?.y.#x
+ this.#x?.y
+ }
+ }
+ `,
+ },
+ mode: "transform",
+ });
+ itBundled("lower/TSLowerPrivateFieldOptionalChain2015NoBundle", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ class Foo {
+ #x
+ foo() {
+ this?.#x.y
+ this?.y.#x
+ this.#x?.y
+ }
+ }
+ `,
+ },
+ unsupportedJSFeatures: "es2015",
+ mode: "transform",
+ });
+ itBundled("lower/TSLowerPrivateStaticMembers2015NoBundle", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ class Foo {
+ static #x
+ static get #y() {}
+ static set #y(x) {}
+ static #z() {}
+ foo() {
+ Foo.#x += 1
+ Foo.#y += 1
+ Foo.#z()
+ }
+ }
+ `,
+ },
+ unsupportedJSFeatures: "es2015",
+ mode: "transform",
+ });
+ itBundled("lower/TSLowerPrivateFieldAndMethodAvoidNameCollision2015", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ class WeakMap {
+ #x
+ }
+ class WeakSet {
+ #y() {}
+ }
+ `,
+ },
+ unsupportedJSFeatures: "es2015",
+ });
+ itBundled("lower/LowerPrivateGetterSetter2015", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Foo {
+ get #foo() { return this.foo }
+ set #bar(val) { this.bar = val }
+ get #prop() { return this.prop }
+ set #prop(val) { this.prop = val }
+ foo(fn) {
+ fn().#foo
+ fn().#bar = 1
+ fn().#prop
+ fn().#prop = 2
+ }
+ unary(fn) {
+ fn().#prop++;
+ fn().#prop--;
+ ++fn().#prop;
+ --fn().#prop;
+ }
+ binary(fn) {
+ fn().#prop = 1;
+ fn().#prop += 1;
+ fn().#prop -= 1;
+ fn().#prop *= 1;
+ fn().#prop /= 1;
+ fn().#prop %= 1;
+ fn().#prop **= 1;
+ fn().#prop <<= 1;
+ fn().#prop >>= 1;
+ fn().#prop >>>= 1;
+ fn().#prop &= 1;
+ fn().#prop |= 1;
+ fn().#prop ^= 1;
+ fn().#prop &&= 1;
+ fn().#prop ||= 1;
+ fn().#prop ??= 1;
+ }
+ }
+ `,
+ },
+ unsupportedJSFeatures: "es2015",
+ });
+ itBundled("lower/LowerPrivateGetterSetter2019", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Foo {
+ get #foo() { return this.foo }
+ set #bar(val) { this.bar = val }
+ get #prop() { return this.prop }
+ set #prop(val) { this.prop = val }
+ foo(fn) {
+ fn().#foo
+ fn().#bar = 1
+ fn().#prop
+ fn().#prop = 2
+ }
+ unary(fn) {
+ fn().#prop++;
+ fn().#prop--;
+ ++fn().#prop;
+ --fn().#prop;
+ }
+ binary(fn) {
+ fn().#prop = 1;
+ fn().#prop += 1;
+ fn().#prop -= 1;
+ fn().#prop *= 1;
+ fn().#prop /= 1;
+ fn().#prop %= 1;
+ fn().#prop **= 1;
+ fn().#prop <<= 1;
+ fn().#prop >>= 1;
+ fn().#prop >>>= 1;
+ fn().#prop &= 1;
+ fn().#prop |= 1;
+ fn().#prop ^= 1;
+ fn().#prop &&= 1;
+ fn().#prop ||= 1;
+ fn().#prop ??= 1;
+ }
+ }
+ `,
+ },
+ unsupportedJSFeatures: "es2019",
+ });
+ itBundled("lower/LowerPrivateGetterSetter2020", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Foo {
+ get #foo() { return this.foo }
+ set #bar(val) { this.bar = val }
+ get #prop() { return this.prop }
+ set #prop(val) { this.prop = val }
+ foo(fn) {
+ fn().#foo
+ fn().#bar = 1
+ fn().#prop
+ fn().#prop = 2
+ }
+ unary(fn) {
+ fn().#prop++;
+ fn().#prop--;
+ ++fn().#prop;
+ --fn().#prop;
+ }
+ binary(fn) {
+ fn().#prop = 1;
+ fn().#prop += 1;
+ fn().#prop -= 1;
+ fn().#prop *= 1;
+ fn().#prop /= 1;
+ fn().#prop %= 1;
+ fn().#prop **= 1;
+ fn().#prop <<= 1;
+ fn().#prop >>= 1;
+ fn().#prop >>>= 1;
+ fn().#prop &= 1;
+ fn().#prop |= 1;
+ fn().#prop ^= 1;
+ fn().#prop &&= 1;
+ fn().#prop ||= 1;
+ fn().#prop ??= 1;
+ }
+ }
+ `,
+ },
+ unsupportedJSFeatures: "es2020",
+ });
+ itBundled("lower/LowerPrivateGetterSetterNext", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ export class Foo {
+ get #foo() { return this.foo }
+ set #bar(val) { this.bar = val }
+ get #prop() { return this.prop }
+ set #prop(val) { this.prop = val }
+ foo(fn) {
+ fn().#foo
+ fn().#bar = 1
+ fn().#prop
+ fn().#prop = 2
+ }
+ unary(fn) {
+ fn().#prop++;
+ fn().#prop--;
+ ++fn().#prop;
+ --fn().#prop;
+ }
+ binary(fn) {
+ fn().#prop = 1;
+ fn().#prop += 1;
+ fn().#prop -= 1;
+ fn().#prop *= 1;
+ fn().#prop /= 1;
+ fn().#prop %= 1;
+ fn().#prop **= 1;
+ fn().#prop <<= 1;
+ fn().#prop >>= 1;
+ fn().#prop >>>= 1;
+ fn().#prop &= 1;
+ fn().#prop |= 1;
+ fn().#prop ^= 1;
+ fn().#prop &&= 1;
+ fn().#prop ||= 1;
+ fn().#prop ??= 1;
+ }
+ }
+ `,
+ },
+ });
+ itBundled("lower/LowerPrivateMethod2019", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Foo {
+ #field
+ #method() {}
+ baseline() {
+ a().foo
+ b().foo(x)
+ c()?.foo(x)
+ d().foo?.(x)
+ e()?.foo?.(x)
+ }
+ privateField() {
+ a().#field
+ b().#field(x)
+ c()?.#field(x)
+ d().#field?.(x)
+ e()?.#field?.(x)
+ f()?.foo.#field(x).bar()
+ }
+ privateMethod() {
+ a().#method
+ b().#method(x)
+ c()?.#method(x)
+ d().#method?.(x)
+ e()?.#method?.(x)
+ f()?.foo.#method(x).bar()
+ }
+ }
+ `,
+ },
+ unsupportedJSFeatures: "es2019",
+ });
+ itBundled("lower/LowerPrivateMethod2020", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Foo {
+ #field
+ #method() {}
+ baseline() {
+ a().foo
+ b().foo(x)
+ c()?.foo(x)
+ d().foo?.(x)
+ e()?.foo?.(x)
+ }
+ privateField() {
+ a().#field
+ b().#field(x)
+ c()?.#field(x)
+ d().#field?.(x)
+ e()?.#field?.(x)
+ f()?.foo.#field(x).bar()
+ }
+ privateMethod() {
+ a().#method
+ b().#method(x)
+ c()?.#method(x)
+ d().#method?.(x)
+ e()?.#method?.(x)
+ f()?.foo.#method(x).bar()
+ }
+ }
+ `,
+ },
+ unsupportedJSFeatures: "es2020",
+ });
+ itBundled("lower/LowerPrivateMethodNext", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ export class Foo {
+ #field
+ #method() {}
+ baseline() {
+ a().foo
+ b().foo(x)
+ c()?.foo(x)
+ d().foo?.(x)
+ e()?.foo?.(x)
+ }
+ privateField() {
+ a().#field
+ b().#field(x)
+ c()?.#field(x)
+ d().#field?.(x)
+ e()?.#field?.(x)
+ f()?.foo.#field(x).bar()
+ }
+ privateMethod() {
+ a().#method
+ b().#method(x)
+ c()?.#method(x)
+ d().#method?.(x)
+ e()?.#method?.(x)
+ f()?.foo.#method(x).bar()
+ }
+ }
+ `,
+ },
+ });
+ itBundled("lower/LowerPrivateClassExpr2020NoBundle", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ export let Foo = class {
+ #field
+ #method() {}
+ static #staticField
+ static #staticMethod() {}
+ foo() {
+ this.#field = this.#method()
+ Foo.#staticField = Foo.#staticMethod()
+ }
+ }
+ `,
+ },
+ unsupportedJSFeatures: "es2020",
+ mode: "transform",
+ });
+ itBundled("lower/LowerPrivateMethodWithModifiers2020", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Foo {
+ *#g() {}
+ async #a() {}
+ async *#ag() {}
+
+ static *#sg() {}
+ static async #sa() {}
+ static async *#sag() {}
+ }
+ `,
+ },
+ unsupportedJSFeatures: "es2020",
+ });
+ itBundled("lower/LowerAsync2016NoBundle", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ async function foo(bar) {
+ await bar
+ return [this, arguments]
+ }
+ class Foo {async foo() {}}
+ export default [
+ foo,
+ Foo,
+ async function() {},
+ async () => {},
+ {async foo() {}},
+ class {async foo() {}},
+ function() {
+ return async (bar) => {
+ await bar
+ return [this, arguments]
+ }
+ },
+ ]
+ `,
+ },
+ unsupportedJSFeatures: "es2016",
+ mode: "transform",
+ });
+ itBundled("lower/LowerAsync2017NoBundle", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ async function foo(bar) {
+ await bar
+ return arguments
+ }
+ class Foo {async foo() {}}
+ export default [
+ foo,
+ Foo,
+ async function() {},
+ async () => {},
+ {async foo() {}},
+ class {async foo() {}},
+ function() {
+ return async (bar) => {
+ await bar
+ return [this, arguments]
+ }
+ },
+ ]
+ `,
+ },
+ unsupportedJSFeatures: "es2017",
+ mode: "transform",
+ });
+ itBundled("lower/LowerAsyncThis2016CommonJS", {
+ // GENERATED
+ files: {
+ "/entry.js": `exports.foo = async () => this`,
+ },
+ unsupportedJSFeatures: "es2016",
+ });
+ itBundled("lower/LowerAsyncThis2016ES6", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ export {bar} from "./other"
+ export let foo = async () => this
+ `,
+ "/other.js": `export let bar = async () => {}`,
+ },
+ unsupportedJSFeatures: "es2016",
+ /* TODO FIX expectedScanLog: `entry.js: DEBUG: Top-level "this" will be replaced with undefined since this file is an ECMAScript module
+ entry.js: NOTE: This file is considered to be an ECMAScript module because of the "export" keyword here:
+ `, */
+ });
+ itBundled("lower/LowerAsyncES5", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import './fn-stmt'
+ import './fn-expr'
+ import './arrow-1'
+ import './arrow-2'
+ import './export-def-1'
+ import './export-def-2'
+ import './obj-method'
+ `,
+ "/fn-stmt.js": `async function foo() {}`,
+ "/fn-expr.js": `(async function() {})`,
+ "/arrow-1.js": `(async () => {})`,
+ "/arrow-2.js": `(async x => {})`,
+ "/export-def-1.js": `export default async function foo() {}`,
+ "/export-def-2.js": `export default async function() {}`,
+ "/obj-method.js": `({async foo() {}})`,
+ },
+ unsupportedJSFeatures: "es5",
+ /* TODO FIX expectedScanLog: `arrow-1.js: ERROR: Transforming async functions to the configured target environment is not supported yet
+ arrow-2.js: ERROR: Transforming async functions to the configured target environment is not supported yet
+ export-def-1.js: ERROR: Transforming async functions to the configured target environment is not supported yet
+ export-def-2.js: ERROR: Transforming async functions to the configured target environment is not supported yet
+ fn-expr.js: ERROR: Transforming async functions to the configured target environment is not supported yet
+ fn-stmt.js: ERROR: Transforming async functions to the configured target environment is not supported yet
+ obj-method.js: ERROR: Transforming async functions to the configured target environment is not supported yet
+ `, */
+ });
+ itBundled("lower/LowerAsyncSuperES2017NoBundle", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Derived extends Base {
+ async test(key) {
+ return [
+ await super.foo,
+ await super[key],
+ await ([super.foo] = [0]),
+ await ([super[key]] = [0]),
+
+ await (super.foo = 1),
+ await (super[key] = 1),
+ await (super.foo += 2),
+ await (super[key] += 2),
+
+ await ++super.foo,
+ await ++super[key],
+ await super.foo++,
+ await super[key]++,
+
+ await super.foo.name,
+ await super[key].name,
+ await super.foo?.name,
+ await super[key]?.name,
+
+ await super.foo(1, 2),
+ await super[key](1, 2),
+ await super.foo?.(1, 2),
+ await super[key]?.(1, 2),
+
+ await (() => super.foo)(),
+ await (() => super[key])(),
+ await (() => super.foo())(),
+ await (() => super[key]())(),
+
+ await super.foo\` + "\`\`" +
+ `,
+ },
+ unsupportedJSFeatures: "es2017",
+ mode: "transform",
+ });
+ itBundled("lower/LowerAsyncSuperES2016NoBundle", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Derived extends Base {
+ async test(key) {
+ return [
+ await super.foo,
+ await super[key],
+ await ([super.foo] = [0]),
+ await ([super[key]] = [0]),
+
+ await (super.foo = 1),
+ await (super[key] = 1),
+ await (super.foo += 2),
+ await (super[key] += 2),
+
+ await ++super.foo,
+ await ++super[key],
+ await super.foo++,
+ await super[key]++,
+
+ await super.foo.name,
+ await super[key].name,
+ await super.foo?.name,
+ await super[key]?.name,
+
+ await super.foo(1, 2),
+ await super[key](1, 2),
+ await super.foo?.(1, 2),
+ await super[key]?.(1, 2),
+
+ await (() => super.foo)(),
+ await (() => super[key])(),
+ await (() => super.foo())(),
+ await (() => super[key]())(),
+
+ await super.foo\` + "\`\`" +
+ `,
+ },
+ unsupportedJSFeatures: "es2016",
+ mode: "transform",
+ });
+ itBundled("lower/LowerStaticAsyncSuperES2021NoBundle", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Derived extends Base {
+ static test = async (key) => {
+ return [
+ await super.foo,
+ await super[key],
+ await ([super.foo] = [0]),
+ await ([super[key]] = [0]),
+
+ await (super.foo = 1),
+ await (super[key] = 1),
+ await (super.foo += 2),
+ await (super[key] += 2),
+
+ await ++super.foo,
+ await ++super[key],
+ await super.foo++,
+ await super[key]++,
+
+ await super.foo.name,
+ await super[key].name,
+ await super.foo?.name,
+ await super[key]?.name,
+
+ await super.foo(1, 2),
+ await super[key](1, 2),
+ await super.foo?.(1, 2),
+ await super[key]?.(1, 2),
+
+ await (() => super.foo)(),
+ await (() => super[key])(),
+ await (() => super.foo())(),
+ await (() => super[key]())(),
+
+ await super.foo\` + "\`\`" +
+ `,
+ },
+ unsupportedJSFeatures: "es2021",
+ mode: "transform",
+ });
+ itBundled("lower/LowerStaticAsyncSuperES2016NoBundle", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Derived extends Base {
+ static test = async (key) => {
+ return [
+ await super.foo,
+ await super[key],
+ await ([super.foo] = [0]),
+ await ([super[key]] = [0]),
+
+ await (super.foo = 1),
+ await (super[key] = 1),
+ await (super.foo += 2),
+ await (super[key] += 2),
+
+ await ++super.foo,
+ await ++super[key],
+ await super.foo++,
+ await super[key]++,
+
+ await super.foo.name,
+ await super[key].name,
+ await super.foo?.name,
+ await super[key]?.name,
+
+ await super.foo(1, 2),
+ await super[key](1, 2),
+ await super.foo?.(1, 2),
+ await super[key]?.(1, 2),
+
+ await (() => super.foo)(),
+ await (() => super[key])(),
+ await (() => super.foo())(),
+ await (() => super[key]())(),
+
+ await super.foo\` + "\`\`" +
+ `,
+ },
+ unsupportedJSFeatures: "es2016",
+ mode: "transform",
+ });
+ itBundled("lower/LowerStaticSuperES2021NoBundle", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Derived extends Base {
+ static test = key => {
+ return [
+ super.foo,
+ super[key],
+ ([super.foo] = [0]),
+ ([super[key]] = [0]),
+
+ (super.foo = 1),
+ (super[key] = 1),
+ (super.foo += 2),
+ (super[key] += 2),
+
+ ++super.foo,
+ ++super[key],
+ super.foo++,
+ super[key]++,
+
+ super.foo.name,
+ super[key].name,
+ super.foo?.name,
+ super[key]?.name,
+
+ super.foo(1, 2),
+ super[key](1, 2),
+ super.foo?.(1, 2),
+ super[key]?.(1, 2),
+
+ (() => super.foo)(),
+ (() => super[key])(),
+ (() => super.foo())(),
+ (() => super[key]())(),
+
+ super.foo\` + "\`\`" +
+ `,
+ },
+ unsupportedJSFeatures: "es2021",
+ mode: "transform",
+ });
+ itBundled("lower/LowerStaticSuperES2016NoBundle", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Derived extends Base {
+ static test = key => {
+ return [
+ super.foo,
+ super[key],
+ ([super.foo] = [0]),
+ ([super[key]] = [0]),
+
+ (super.foo = 1),
+ (super[key] = 1),
+ (super.foo += 2),
+ (super[key] += 2),
+
+ ++super.foo,
+ ++super[key],
+ super.foo++,
+ super[key]++,
+
+ super.foo.name,
+ super[key].name,
+ super.foo?.name,
+ super[key]?.name,
+
+ super.foo(1, 2),
+ super[key](1, 2),
+ super.foo?.(1, 2),
+ super[key]?.(1, 2),
+
+ (() => super.foo)(),
+ (() => super[key])(),
+ (() => super.foo())(),
+ (() => super[key]())(),
+
+ super.foo\` + "\`\`" +
+ `,
+ },
+ unsupportedJSFeatures: "es2016",
+ mode: "transform",
+ });
+ itBundled("lower/LowerAsyncArrowSuperES2016", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ export { default as foo1 } from "./foo1"
+ export { default as foo2 } from "./foo2"
+ export { default as foo3 } from "./foo3"
+ export { default as foo4 } from "./foo4"
+ export { default as bar1 } from "./bar1"
+ export { default as bar2 } from "./bar2"
+ export { default as bar3 } from "./bar3"
+ export { default as bar4 } from "./bar4"
+ export { default as baz1 } from "./baz1"
+ export { default as baz2 } from "./baz2"
+ import "./outer"
+ `,
+ "/foo1.js": `export default class extends x { foo1() { return async () => super.foo('foo1') } }`,
+ "/foo2.js": `export default class extends x { foo2() { return async () => () => super.foo('foo2') } }`,
+ "/foo3.js": `export default class extends x { foo3() { return () => async () => super.foo('foo3') } }`,
+ "/foo4.js": `export default class extends x { foo4() { return async () => async () => super.foo('foo4') } }`,
+ "/bar1.js": `export default class extends x { bar1 = async () => super.foo('bar1') }`,
+ "/bar2.js": `export default class extends x { bar2 = async () => () => super.foo('bar2') }`,
+ "/bar3.js": `export default class extends x { bar3 = () => async () => super.foo('bar3') }`,
+ "/bar4.js": `export default class extends x { bar4 = async () => async () => super.foo('bar4') }`,
+ "/baz1.js": `export default class extends x { async baz1() { return () => super.foo('baz1') } }`,
+ "/baz2.js": `export default class extends x { async baz2() { return () => () => super.foo('baz2') } }`,
+ "/outer.js": /* js */ `
+ // Helper functions for "super" shouldn't be inserted into this outer function
+ export default (async function () {
+ class y extends z {
+ foo = async () => super.foo()
+ }
+ await new y().foo()()
+ })()
+ `,
+ },
+ unsupportedJSFeatures: "es2016",
+ });
+ itBundled("lower/LowerAsyncArrowSuperSetterES2016", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ export { default as foo1 } from "./foo1"
+ export { default as foo2 } from "./foo2"
+ export { default as foo3 } from "./foo3"
+ export { default as foo4 } from "./foo4"
+ export { default as bar1 } from "./bar1"
+ export { default as bar2 } from "./bar2"
+ export { default as bar3 } from "./bar3"
+ export { default as bar4 } from "./bar4"
+ export { default as baz1 } from "./baz1"
+ export { default as baz2 } from "./baz2"
+ import "./outer"
+ `,
+ "/foo1.js": `export default class extends x { foo1() { return async () => super.foo = 'foo1' } }`,
+ "/foo2.js": `export default class extends x { foo2() { return async () => () => super.foo = 'foo2' } }`,
+ "/foo3.js": `export default class extends x { foo3() { return () => async () => super.foo = 'foo3' } }`,
+ "/foo4.js": `export default class extends x { foo4() { return async () => async () => super.foo = 'foo4' } }`,
+ "/bar1.js": `export default class extends x { bar1 = async () => super.foo = 'bar1' }`,
+ "/bar2.js": `export default class extends x { bar2 = async () => () => super.foo = 'bar2' }`,
+ "/bar3.js": `export default class extends x { bar3 = () => async () => super.foo = 'bar3' }`,
+ "/bar4.js": `export default class extends x { bar4 = async () => async () => super.foo = 'bar4' }`,
+ "/baz1.js": `export default class extends x { async baz1() { return () => super.foo = 'baz1' } }`,
+ "/baz2.js": `export default class extends x { async baz2() { return () => () => super.foo = 'baz2' } }`,
+ "/outer.js": /* js */ `
+ // Helper functions for "super" shouldn't be inserted into this outer function
+ export default (async function () {
+ class y extends z {
+ foo = async () => super.foo = 'foo'
+ }
+ await new y().foo()()
+ })()
+ `,
+ },
+ unsupportedJSFeatures: "es2016",
+ });
+ itBundled("lower/LowerStaticAsyncArrowSuperES2016", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ export { default as foo1 } from "./foo1"
+ export { default as foo2 } from "./foo2"
+ export { default as foo3 } from "./foo3"
+ export { default as foo4 } from "./foo4"
+ export { default as bar1 } from "./bar1"
+ export { default as bar2 } from "./bar2"
+ export { default as bar3 } from "./bar3"
+ export { default as bar4 } from "./bar4"
+ export { default as baz1 } from "./baz1"
+ export { default as baz2 } from "./baz2"
+ import "./outer"
+ `,
+ "/foo1.js": `export default class extends x { static foo1() { return async () => super.foo('foo1') } }`,
+ "/foo2.js": `export default class extends x { static foo2() { return async () => () => super.foo('foo2') } }`,
+ "/foo3.js": `export default class extends x { static foo3() { return () => async () => super.foo('foo3') } }`,
+ "/foo4.js": `export default class extends x { static foo4() { return async () => async () => super.foo('foo4') } }`,
+ "/bar1.js": `export default class extends x { static bar1 = async () => super.foo('bar1') }`,
+ "/bar2.js": `export default class extends x { static bar2 = async () => () => super.foo('bar2') }`,
+ "/bar3.js": `export default class extends x { static bar3 = () => async () => super.foo('bar3') }`,
+ "/bar4.js": `export default class extends x { static bar4 = async () => async () => super.foo('bar4') }`,
+ "/baz1.js": `export default class extends x { static async baz1() { return () => super.foo('baz1') } }`,
+ "/baz2.js": `export default class extends x { static async baz2() { return () => () => super.foo('baz2') } }`,
+ "/outer.js": /* js */ `
+ // Helper functions for "super" shouldn't be inserted into this outer function
+ export default (async function () {
+ class y extends z {
+ static foo = async () => super.foo()
+ }
+ await y.foo()()
+ })()
+ `,
+ },
+ unsupportedJSFeatures: "es2016",
+ });
+ itBundled("lower/LowerStaticAsyncArrowSuperSetterES2016", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ export { default as foo1 } from "./foo1"
+ export { default as foo2 } from "./foo2"
+ export { default as foo3 } from "./foo3"
+ export { default as foo4 } from "./foo4"
+ export { default as bar1 } from "./bar1"
+ export { default as bar2 } from "./bar2"
+ export { default as bar3 } from "./bar3"
+ export { default as bar4 } from "./bar4"
+ export { default as baz1 } from "./baz1"
+ export { default as baz2 } from "./baz2"
+ import "./outer"
+ `,
+ "/foo1.js": `export default class extends x { static foo1() { return async () => super.foo = 'foo1' } }`,
+ "/foo2.js": `export default class extends x { static foo2() { return async () => () => super.foo = 'foo2' } }`,
+ "/foo3.js": `export default class extends x { static foo3() { return () => async () => super.foo = 'foo3' } }`,
+ "/foo4.js": `export default class extends x { static foo4() { return async () => async () => super.foo = 'foo4' } }`,
+ "/bar1.js": `export default class extends x { static bar1 = async () => super.foo = 'bar1' }`,
+ "/bar2.js": `export default class extends x { static bar2 = async () => () => super.foo = 'bar2' }`,
+ "/bar3.js": `export default class extends x { static bar3 = () => async () => super.foo = 'bar3' }`,
+ "/bar4.js": `export default class extends x { static bar4 = async () => async () => super.foo = 'bar4' }`,
+ "/baz1.js": `export default class extends x { static async baz1() { return () => super.foo = 'baz1' } }`,
+ "/baz2.js": `export default class extends x { static async baz2() { return () => () => super.foo = 'baz2' } }`,
+ "/outer.js": /* js */ `
+ // Helper functions for "super" shouldn't be inserted into this outer function
+ export default (async function () {
+ class y extends z {
+ static foo = async () => super.foo = 'foo'
+ }
+ await y.foo()()
+ })()
+ `,
+ },
+ unsupportedJSFeatures: "es2016",
+ });
+ itBundled("lower/LowerPrivateSuperES2022", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ export { default as foo1 } from "./foo1"
+ export { default as foo2 } from "./foo2"
+ export { default as foo3 } from "./foo3"
+ export { default as foo4 } from "./foo4"
+ export { default as foo5 } from "./foo5"
+ export { default as foo6 } from "./foo6"
+ export { default as foo7 } from "./foo7"
+ export { default as foo8 } from "./foo8"
+ `,
+ "/foo1.js": `export default class extends x { #foo() { super.foo() } }`,
+ "/foo2.js": `export default class extends x { #foo() { super.foo++ } }`,
+ "/foo3.js": `export default class extends x { static #foo() { super.foo() } }`,
+ "/foo4.js": `export default class extends x { static #foo() { super.foo++ } }`,
+ "/foo5.js": `export default class extends x { #foo = () => { super.foo() } }`,
+ "/foo6.js": `export default class extends x { #foo = () => { super.foo++ } }`,
+ "/foo7.js": `export default class extends x { static #foo = () => { super.foo() } }`,
+ "/foo8.js": `export default class extends x { static #foo = () => { super.foo++ } }`,
+ },
+ unsupportedJSFeatures: "es2022",
+ });
+ itBundled("lower/LowerPrivateSuperES2021", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ export { default as foo1 } from "./foo1"
+ export { default as foo2 } from "./foo2"
+ export { default as foo3 } from "./foo3"
+ export { default as foo4 } from "./foo4"
+ export { default as foo5 } from "./foo5"
+ export { default as foo6 } from "./foo6"
+ export { default as foo7 } from "./foo7"
+ export { default as foo8 } from "./foo8"
+ `,
+ "/foo1.js": `export default class extends x { #foo() { super.foo() } }`,
+ "/foo2.js": `export default class extends x { #foo() { super.foo++ } }`,
+ "/foo3.js": `export default class extends x { static #foo() { super.foo() } }`,
+ "/foo4.js": `export default class extends x { static #foo() { super.foo++ } }`,
+ "/foo5.js": `export default class extends x { #foo = () => { super.foo() } }`,
+ "/foo6.js": `export default class extends x { #foo = () => { super.foo++ } }`,
+ "/foo7.js": `export default class extends x { static #foo = () => { super.foo() } }`,
+ "/foo8.js": `export default class extends x { static #foo = () => { super.foo++ } }`,
+ },
+ unsupportedJSFeatures: "es2021",
+ });
+ itBundled("lower/LowerPrivateSuperStaticBundleIssue2158", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ export class Foo extends Object {
+ static FOO;
+ constructor() {
+ super();
+ }
+ #foo;
+ }
+ `,
+ },
+ });
+ itBundled("lower/LowerClassField2020NoBundle", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Foo {
+ #foo = 123
+ #bar
+ foo = 123
+ bar
+ static #s_foo = 123
+ static #s_bar
+ static s_foo = 123
+ static s_bar
+ }
+ `,
+ },
+ unsupportedJSFeatures: "es2020",
+ mode: "transform",
+ });
+ itBundled("lower/LowerClassFieldNextNoBundle", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Foo {
+ #foo = 123
+ #bar
+ foo = 123
+ bar
+ static #s_foo = 123
+ static #s_bar
+ static s_foo = 123
+ static s_bar
+ }
+ `,
+ },
+ mode: "transform",
+ });
+ itBundled("lower/TSLowerClassField2020NoBundle", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ class Foo {
+ #foo = 123
+ #bar
+ foo = 123
+ bar
+ static #s_foo = 123
+ static #s_bar
+ static s_foo = 123
+ static s_bar
+ }
+ `,
+ },
+ unsupportedJSFeatures: "es2020",
+ mode: "transform",
+ });
+ itBundled("lower/TSLowerClassPrivateFieldNextNoBundle", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ class Foo {
+ #foo = 123
+ #bar
+ foo = 123
+ bar
+ static #s_foo = 123
+ static #s_bar
+ static s_foo = 123
+ static s_bar
+ }
+ `,
+ },
+ mode: "transform",
+ });
+ itBundled("lower/LowerClassFieldStrictTsconfigJson2020", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import loose from './loose'
+ import strict from './strict'
+ console.log(loose, strict)
+ `,
+ "/loose/index.js": /* js */ `
+ export default class {
+ foo
+ }
+ `,
+ "/loose/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "useDefineForClassFields": false
+ }
+ }
+ `,
+ "/strict/index.js": /* js */ `
+ export default class {
+ foo
+ }
+ `,
+ "/strict/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "useDefineForClassFields": true
+ }
+ }
+ `,
+ },
+ unsupportedJSFeatures: "es2020",
+ });
+ itBundled("lower/TSLowerClassFieldStrictTsconfigJson2020", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import loose from './loose'
+ import strict from './strict'
+ console.log(loose, strict)
+ `,
+ "/loose/index.ts": /* ts */ `
+ export default class {
+ foo
+ }
+ `,
+ "/loose/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "useDefineForClassFields": false
+ }
+ }
+ `,
+ "/strict/index.ts": /* ts */ `
+ export default class {
+ foo
+ }
+ `,
+ "/strict/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "useDefineForClassFields": true
+ }
+ }
+ `,
+ },
+ unsupportedJSFeatures: "es2020",
+ });
+ itBundled("lower/TSLowerObjectRest2017NoBundle", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ const { ...local_const } = {};
+ let { ...local_let } = {};
+ var { ...local_var } = {};
+ let arrow_fn = ({ ...x }) => { };
+ let fn_expr = function ({ ...x } = default_value) {};
+ let class_expr = class { method(x, ...[y, { ...z }]) {} };
+
+ function fn_stmt({ a = b(), ...x }, { c = d(), ...y }) {}
+ class class_stmt { method({ ...x }) {} }
+ namespace ns { export let { ...x } = {} }
+ try { } catch ({ ...catch_clause }) {}
+
+ for (const { ...for_in_const } in { abc }) {}
+ for (let { ...for_in_let } in { abc }) {}
+ for (var { ...for_in_var } in { abc }) ;
+ for (const { ...for_of_const } of [{}]) ;
+ for (let { ...for_of_let } of [{}]) x()
+ for (var { ...for_of_var } of [{}]) x()
+ for (const { ...for_const } = {}; x; x = null) {}
+ for (let { ...for_let } = {}; x; x = null) {}
+ for (var { ...for_var } = {}; x; x = null) {}
+ for ({ ...x } in { abc }) {}
+ for ({ ...x } of [{}]) {}
+ for ({ ...x } = {}; x; x = null) {}
+
+ ({ ...assign } = {});
+ ({ obj_method({ ...x }) {} });
+
+ // Check for used return values
+ ({ ...x } = x);
+ for ({ ...x } = x; 0; ) ;
+ console.log({ ...x } = x);
+ console.log({ x, ...xx } = { x });
+ console.log({ x: { ...xx } } = { x });
+ `,
+ },
+ unsupportedJSFeatures: "es2017",
+ mode: "transform",
+ });
+ itBundled("lower/TSLowerObjectRest2018NoBundle", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ const { ...local_const } = {};
+ let { ...local_let } = {};
+ var { ...local_var } = {};
+ let arrow_fn = ({ ...x }) => { };
+ let fn_expr = function ({ ...x } = default_value) {};
+ let class_expr = class { method(x, ...[y, { ...z }]) {} };
+
+ function fn_stmt({ a = b(), ...x }, { c = d(), ...y }) {}
+ class class_stmt { method({ ...x }) {} }
+ namespace ns { export let { ...x } = {} }
+ try { } catch ({ ...catch_clause }) {}
+
+ for (const { ...for_in_const } in { abc }) {}
+ for (let { ...for_in_let } in { abc }) {}
+ for (var { ...for_in_var } in { abc }) ;
+ for (const { ...for_of_const } of [{}]) ;
+ for (let { ...for_of_let } of [{}]) x()
+ for (var { ...for_of_var } of [{}]) x()
+ for (const { ...for_const } = {}; x; x = null) {}
+ for (let { ...for_let } = {}; x; x = null) {}
+ for (var { ...for_var } = {}; x; x = null) {}
+ for ({ ...x } in { abc }) {}
+ for ({ ...x } of [{}]) {}
+ for ({ ...x } = {}; x; x = null) {}
+
+ ({ ...assign } = {});
+ ({ obj_method({ ...x }) {} });
+
+ // Check for used return values
+ ({ ...x } = x);
+ for ({ ...x } = x; 0; ) ;
+ console.log({ ...x } = x);
+ console.log({ x, ...xx } = { x });
+ console.log({ x: { ...xx } } = { x });
+ `,
+ },
+ unsupportedJSFeatures: "es2018",
+ mode: "transform",
+ });
+ itBundled("lower/ClassSuperThisIssue242NoBundle", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ export class A {}
+
+ export class B extends A {
+ #e: string
+ constructor(c: { d: any }) {
+ super()
+ this.#e = c.d ?? 'test'
+ }
+ f() {
+ return this.#e
+ }
+ }
+ `,
+ },
+ unsupportedJSFeatures: "es2019",
+ mode: "transform",
+ });
+ itBundled("lower/LowerExportStarAsNameCollisionNoBundle", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ export * as ns from 'path'
+ let ns = 123
+ export {ns as sn}
+ `,
+ },
+ unsupportedJSFeatures: "es2019",
+ mode: "transform",
+ });
+ itBundled("lower/LowerExportStarAsNameCollision", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import * as test from './nested'
+ console.log(test.foo, test.oof)
+ export * as ns from 'path1'
+ let ns = 123
+ export {ns as sn}
+ `,
+ "/nested.js": /* js */ `
+ export * as foo from 'path2'
+ let foo = 123
+ export {foo as oof}
+ `,
+ },
+ unsupportedJSFeatures: "es2019",
+ });
+ itBundled("lower/LowerStrictModeSyntax", {
+ // GENERATED
+ files: {
+ "/entry.js": `import './for-in'`,
+ "/for-in.js": /* js */ `
+ if (test)
+ for (var a = b in {}) ;
+ for (var x = y in {}) ;
+ `,
+ },
+ format: "esm",
+ });
+ itBundled("lower/LowerForbidStrictModeSyntax", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import './with'
+ import './delete-1'
+ import './delete-2'
+ import './delete-3'
+ `,
+ "/with.js": `with (x) y`,
+ "/delete-1.js": `delete x`,
+ "/delete-2.js": `delete (y)`,
+ "/delete-3.js": `delete (1 ? z : z)`,
+ },
+ format: "esm",
+ /* TODO FIX expectedScanLog: `delete-1.js: ERROR: Delete of a bare identifier cannot be used with the "esm" output format due to strict mode
+ delete-2.js: ERROR: Delete of a bare identifier cannot be used with the "esm" output format due to strict mode
+ with.js: ERROR: With statements cannot be used with the "esm" output format due to strict mode
+ `, */
+ });
+ itBundled("lower/LowerPrivateClassFieldOrder", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Foo {
+ #foo = 123 // This must be set before "bar" is initialized
+ bar = this.#foo
+ }
+ console.log(new Foo().bar === 123)
+ `,
+ },
+ mode: "passthrough",
+ });
+ itBundled("lower/LowerPrivateClassMethodOrder", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Foo {
+ bar = this.#foo()
+ #foo() { return 123 } // This must be set before "bar" is initialized
+ }
+ console.log(new Foo().bar === 123)
+ `,
+ },
+ mode: "passthrough",
+ });
+ itBundled("lower/LowerPrivateClassAccessorOrder", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Foo {
+ bar = this.#foo
+ get #foo() { return 123 } // This must be set before "bar" is initialized
+ }
+ console.log(new Foo().bar === 123)
+ `,
+ },
+ mode: "passthrough",
+ });
+ itBundled("lower/LowerPrivateClassStaticFieldOrder", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Foo {
+ static #foo = 123 // This must be set before "bar" is initialized
+ static bar = Foo.#foo
+ }
+ console.log(Foo.bar === 123)
+
+ class FooThis {
+ static #foo = 123 // This must be set before "bar" is initialized
+ static bar = this.#foo
+ }
+ console.log(FooThis.bar === 123)
+ `,
+ },
+ mode: "passthrough",
+ });
+ itBundled("lower/LowerPrivateClassStaticMethodOrder", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Foo {
+ static bar = Foo.#foo()
+ static #foo() { return 123 } // This must be set before "bar" is initialized
+ }
+ console.log(Foo.bar === 123)
+
+ class FooThis {
+ static bar = this.#foo()
+ static #foo() { return 123 } // This must be set before "bar" is initialized
+ }
+ console.log(FooThis.bar === 123)
+ `,
+ },
+ mode: "passthrough",
+ });
+ itBundled("lower/LowerPrivateClassStaticAccessorOrder", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Foo {
+ static bar = Foo.#foo
+ static get #foo() { return 123 } // This must be set before "bar" is initialized
+ }
+ console.log(Foo.bar === 123)
+
+ class FooThis {
+ static bar = this.#foo
+ static get #foo() { return 123 } // This must be set before "bar" is initialized
+ }
+ console.log(FooThis.bar === 123)
+ `,
+ },
+ mode: "passthrough",
+ });
+ itBundled("lower/LowerPrivateClassBrandCheckUnsupported", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Foo {
+ #foo
+ #bar
+ baz() {
+ return [
+ this.#foo,
+ this.#bar,
+ #foo in this,
+ ]
+ }
+ }
+ `,
+ },
+ mode: "passthrough",
+ });
+ itBundled("lower/LowerPrivateClassBrandCheckSupported", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class Foo {
+ #foo
+ #bar
+ baz() {
+ return [
+ this.#foo,
+ this.#bar,
+ #foo in this,
+ ]
+ }
+ }
+ `,
+ },
+ mode: "passthrough",
+ });
+ itBundled("lower/LowerTemplateObject", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ x = () => [
+ tag\` + "\`x\`" +
+ `,
+ },
+ mode: "passthrough",
+ });
+ itBundled("lower/LowerPrivateClassFieldStaticIssue1424", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class T {
+ #a() { return 'a'; }
+ #b() { return 'b'; }
+ static c;
+ d() { console.log(this.#a()); }
+ }
+ new T().d();
+ `,
+ },
+ });
+ itBundled("lower/LowerNullishCoalescingAssignmentIssue1493", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ export class A {
+ #a;
+ f() {
+ this.#a ??= 1;
+ }
+ }
+ `,
+ },
+ });
+ itBundled("lower/StaticClassBlockESNext", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class A {
+ static {}
+ static {
+ this.thisField++
+ A.classField++
+ super.superField = super.superField + 1
+ super.superField++
+ }
+ }
+ let B = class {
+ static {}
+ static {
+ this.thisField++
+ super.superField = super.superField + 1
+ super.superField++
+ }
+ }
+ `,
+ },
+ });
+ itBundled("lower/StaticClassBlockES2021", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ class A {
+ static {}
+ static {
+ this.thisField++
+ A.classField++
+ super.superField = super.superField + 1
+ super.superField++
+ }
+ }
+ let B = class {
+ static {}
+ static {
+ this.thisField++
+ super.superField = super.superField + 1
+ super.superField++
+ }
+ }
+ `,
+ },
+ });
+ itBundled("lower/LowerRegExpNameCollision", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ export function foo(RegExp) {
+ return new RegExp(/./d, 'd')
+ }
+ `,
+ },
+ });
+ itBundled("lower/LowerForAwait2017", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ export default [
+ async () => { for await (x of y) z(x) },
+ async () => { for await (x.y of y) z(x) },
+ async () => { for await (let x of y) z(x) },
+ async () => { for await (const x of y) z(x) },
+ ]
+ `,
+ },
+ mode: "passthrough",
+ });
+ itBundled("lower/LowerForAwait2015", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ export default [
+ async () => { for await (x of y) z(x) },
+ async () => { for await (x.y of y) z(x) },
+ async () => { for await (let x of y) z(x) },
+ async () => { for await (const x of y) z(x) },
+ ]
+ `,
+ },
+ mode: "passthrough",
+ });
+ itBundled("lower/LowerNestedFunctionDirectEval", {
+ // GENERATED
+ files: {
+ "/1.js": `if (foo) { function x() {} }`,
+ "/2.js": `if (foo) { function x() {} eval('') }`,
+ "/3.js": `if (foo) { function x() {} if (bar) { eval('') } }`,
+ "/4.js": `if (foo) { eval(''); function x() {} }`,
+ "/5.js": `'use strict'; if (foo) { function x() {} }`,
+ "/6.js": `'use strict'; if (foo) { function x() {} eval('') }`,
+ "/7.js": `'use strict'; if (foo) { function x() {} if (bar) { eval('') } }`,
+ "/8.js": `'use strict'; if (foo) { eval(''); function x() {} }`,
+ },
+ entryPoints: ["/1.js", "/2.js", "/3.js", "/4.js", "/5.js", "/6.js", "/7.js", "/8.js"],
+ mode: "passthrough",
+ });
+});
diff --git a/test/bundler/esbuild/packagejson.test.ts b/test/bundler/esbuild/packagejson.test.ts
new file mode 100644
index 000000000..0cdb46569
--- /dev/null
+++ b/test/bundler/esbuild/packagejson.test.ts
@@ -0,0 +1,1879 @@
+import { expectBundled, itBundled, testForFile } from "../expectBundled";
+var { describe, test, expect } = testForFile(import.meta.path);
+
+// Tests ported from:
+// https://github.com/evanw/esbuild/blob/main/internal/bundler_tests/bundler_packagejson_test.go
+
+// For debug, all files are written to $TEMP/bun-bundle-tests/packagejson
+
+describe("bundler", () => {
+ return;
+ itBundled("packagejson/PackageJsonMain", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import fn from 'demo-pkg'
+ console.log(fn())
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "main": "./custom-main.js"
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/custom-main.js": /* js */ `
+ module.exports = function() {
+ return 123
+ }
+ `,
+ },
+ });
+ itBundled("packagejson/PackageJsonBadMain", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import fn from 'demo-pkg'
+ console.log(fn())
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "main": "./does-not-exist.js"
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/index.js": /* js */ `
+ module.exports = function() {
+ return 123
+ }
+ `,
+ },
+ });
+ itBundled("packagejson/PackageJsonSyntaxErrorComment", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import fn from 'demo-pkg'
+ console.log(fn())
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ // Single-line comment
+ "a": 1
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/index.js": /* js */ `
+ module.exports = function() {
+ return 123
+ }
+ `,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/node_modules/demo-pkg/package.json: ERROR: JSON does not support comments
+ `, */
+ });
+ itBundled("packagejson/PackageJsonSyntaxErrorTrailingComma", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import fn from 'demo-pkg'
+ console.log(fn())
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "a": 1,
+ "b": 2,
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/index.js": /* js */ `
+ module.exports = function() {
+ return 123
+ }
+ `,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/node_modules/demo-pkg/package.json: ERROR: JSON does not support trailing commas
+ `, */
+ });
+ itBundled("packagejson/PackageJsonModule", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import fn from 'demo-pkg'
+ console.log(fn())
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "main": "./main.js",
+ "module": "./main.esm.js"
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/main.js": /* js */ `
+ module.exports = function() {
+ return 123
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/main.esm.js": /* js */ `
+ export default function() {
+ return 123
+ }
+ `,
+ },
+ });
+ itBundled("packagejson/PackageJsonBrowserString", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import fn from 'demo-pkg'
+ console.log(fn())
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "browser": "./browser"
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/browser.js": /* js */ `
+ module.exports = function() {
+ return 123
+ }
+ `,
+ },
+ });
+ itBundled("packagejson/PackageJsonBrowserMapRelativeToRelative", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import fn from 'demo-pkg'
+ console.log(fn())
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "main": "./main",
+ "browser": {
+ "./main.js": "./main-browser",
+ "./lib/util.js": "./lib/util-browser"
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/main.js": /* js */ `
+ const util = require('./lib/util')
+ module.exports = function() {
+ return ['main', util]
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/main-browser.js": /* js */ `
+ const util = require('./lib/util')
+ module.exports = function() {
+ return ['main-browser', util]
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/lib/util.js": `module.exports = 'util'`,
+ "/Users/user/project/node_modules/demo-pkg/lib/util-browser.js": `module.exports = 'util-browser'`,
+ },
+ });
+ itBundled("packagejson/PackageJsonBrowserMapRelativeToModule", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import fn from 'demo-pkg'
+ console.log(fn())
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "main": "./main",
+ "browser": {
+ "./util.js": "util-browser"
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/main.js": /* js */ `
+ const util = require('./util')
+ module.exports = function() {
+ return ['main', util]
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/util.js": `module.exports = 'util'`,
+ "/Users/user/project/node_modules/util-browser/index.js": `module.exports = 'util-browser'`,
+ },
+ });
+ itBundled("packagejson/PackageJsonBrowserMapRelativeDisabled", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import fn from 'demo-pkg'
+ console.log(fn())
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "main": "./main",
+ "browser": {
+ "./util-node.js": false
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/main.js": /* js */ `
+ const util = require('./util-node')
+ module.exports = function(obj) {
+ return util.inspect(obj)
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/util-node.js": `module.exports = require('util')`,
+ },
+ });
+ itBundled("packagejson/PackageJsonBrowserMapModuleToRelative", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import fn from 'demo-pkg'
+ console.log(fn())
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "browser": {
+ "node-pkg": "./node-pkg-browser"
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/node-pkg-browser.js": /* js */ `
+ module.exports = function() {
+ return 123
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/index.js": /* js */ `
+ const fn = require('node-pkg')
+ module.exports = function() {
+ return fn()
+ }
+ `,
+ "/Users/user/project/node_modules/node-pkg/index.js": /* js */ `
+ module.exports = function() {
+ return 234
+ }
+ `,
+ },
+ });
+ itBundled("packagejson/PackageJsonBrowserMapModuleToModule", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import fn from 'demo-pkg'
+ console.log(fn())
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "browser": {
+ "node-pkg": "node-pkg-browser"
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/node-pkg-browser/index.js": /* js */ `
+ module.exports = function() {
+ return 123
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/index.js": /* js */ `
+ const fn = require('node-pkg')
+ module.exports = function() {
+ return fn()
+ }
+ `,
+ "/Users/user/project/node_modules/node-pkg/index.js": /* js */ `
+ module.exports = function() {
+ return 234
+ }
+ `,
+ },
+ });
+ itBundled("packagejson/PackageJsonBrowserMapModuleDisabled", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import fn from 'demo-pkg'
+ console.log(fn())
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "browser": {
+ "node-pkg": false
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/index.js": /* js */ `
+ const fn = require('node-pkg')
+ module.exports = function() {
+ return fn()
+ }
+ `,
+ "/Users/user/project/node_modules/node-pkg/index.js": /* js */ `
+ module.exports = function() {
+ return 234
+ }
+ `,
+ },
+ });
+ itBundled("packagejson/PackageJsonBrowserMapNativeModuleDisabled", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import fn from 'demo-pkg'
+ console.log(fn())
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "browser": {
+ "fs": false
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/index.js": /* js */ `
+ const fs = require('fs')
+ module.exports = function() {
+ return fs.readFile()
+ }
+ `,
+ },
+ });
+ itBundled("packagejson/PackageJsonBrowserMapAvoidMissing", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": `import 'component-classes'`,
+ "/Users/user/project/node_modules/component-classes/package.json": /* json */ `
+ {
+ "browser": {
+ "indexof": "component-indexof"
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/component-classes/index.js": /* js */ `
+ try {
+ var index = require('indexof');
+ } catch (err) {
+ var index = require('component-indexof');
+ }
+ `,
+ "/Users/user/project/node_modules/component-indexof/index.js": /* js */ `
+ module.exports = function() {
+ return 234
+ }
+ `,
+ },
+ });
+ itBundled("packagejson/PackageJsonBrowserOverModuleBrowser", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import fn from 'demo-pkg'
+ console.log(fn())
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "main": "./main.js",
+ "module": "./main.esm.js",
+ "browser": "./main.browser.js"
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/main.js": /* js */ `
+ module.exports = function() {
+ return 123
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/main.esm.js": /* js */ `
+ export default function() {
+ return 123
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/main.browser.js": /* js */ `
+ module.exports = function() {
+ return 123
+ }
+ `,
+ },
+ platform: "browser",
+ });
+ itBundled("packagejson/PackageJsonBrowserOverMainNode", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import fn from 'demo-pkg'
+ console.log(fn())
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "main": "./main.js",
+ "module": "./main.esm.js",
+ "browser": "./main.browser.js"
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/main.js": /* js */ `
+ module.exports = function() {
+ return 123
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/main.esm.js": /* js */ `
+ export default function() {
+ return 123
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/main.browser.js": /* js */ `
+ module.exports = function() {
+ return 123
+ }
+ `,
+ },
+ platform: "node",
+ });
+ itBundled("packagejson/PackageJsonBrowserWithModuleBrowser", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import fn from 'demo-pkg'
+ console.log(fn())
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "main": "./main.js",
+ "module": "./main.esm.js",
+ "browser": {
+ "./main.js": "./main.browser.js",
+ "./main.esm.js": "./main.browser.esm.js"
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/main.js": /* js */ `
+ module.exports = function() {
+ return 123
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/main.esm.js": /* js */ `
+ export default function() {
+ return 123
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/main.browser.js": /* js */ `
+ module.exports = function() {
+ return 123
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/main.browser.esm.js": /* js */ `
+ export default function() {
+ return 123
+ }
+ `,
+ },
+ platform: "browser",
+ });
+ itBundled("packagejson/PackageJsonBrowserWithMainNode", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import fn from 'demo-pkg'
+ console.log(fn())
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "main": "./main.js",
+ "module": "./main.esm.js",
+ "browser": {
+ "./main.js": "./main.browser.js",
+ "./main.esm.js": "./main.browser.esm.js"
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/main.js": /* js */ `
+ module.exports = function() {
+ return 123
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/main.esm.js": /* js */ `
+ export default function() {
+ return 123
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/main.browser.js": /* js */ `
+ module.exports = function() {
+ return 123
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/main.browser.esm.js": /* js */ `
+ export default function() {
+ return 123
+ }
+ `,
+ },
+ platform: "node",
+ });
+ itBundled("packagejson/PackageJsonBrowserNodeModulesNoExt", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import {browser as a} from 'demo-pkg/no-ext'
+ import {node as b} from 'demo-pkg/no-ext.js'
+ import {browser as c} from 'demo-pkg/ext'
+ import {browser as d} from 'demo-pkg/ext.js'
+ console.log(a)
+ console.log(b)
+ console.log(c)
+ console.log(d)
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "browser": {
+ "./no-ext": "./no-ext-browser.js",
+ "./ext.js": "./ext-browser.js"
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/no-ext.js": `export let node = 'node'`,
+ "/Users/user/project/node_modules/demo-pkg/no-ext-browser.js": `export let browser = 'browser'`,
+ "/Users/user/project/node_modules/demo-pkg/ext.js": `export let node = 'node'`,
+ "/Users/user/project/node_modules/demo-pkg/ext-browser.js": `export let browser = 'browser'`,
+ },
+ });
+ itBundled("packagejson/PackageJsonBrowserNodeModulesIndexNoExt", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import {browser as a} from 'demo-pkg/no-ext'
+ import {node as b} from 'demo-pkg/no-ext/index.js'
+ import {browser as c} from 'demo-pkg/ext'
+ import {browser as d} from 'demo-pkg/ext/index.js'
+ console.log(a)
+ console.log(b)
+ console.log(c)
+ console.log(d)
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "browser": {
+ "./no-ext": "./no-ext-browser/index.js",
+ "./ext/index.js": "./ext-browser/index.js"
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/no-ext/index.js": `export let node = 'node'`,
+ "/Users/user/project/node_modules/demo-pkg/no-ext-browser/index.js": `export let browser = 'browser'`,
+ "/Users/user/project/node_modules/demo-pkg/ext/index.js": `export let node = 'node'`,
+ "/Users/user/project/node_modules/demo-pkg/ext-browser/index.js": `export let browser = 'browser'`,
+ },
+ });
+ itBundled("packagejson/PackageJsonBrowserNoExt", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import {browser as a} from './demo-pkg/no-ext'
+ import {node as b} from './demo-pkg/no-ext.js'
+ import {browser as c} from './demo-pkg/ext'
+ import {browser as d} from './demo-pkg/ext.js'
+ console.log(a)
+ console.log(b)
+ console.log(c)
+ console.log(d)
+ `,
+ "/Users/user/project/src/demo-pkg/package.json": /* json */ `
+ {
+ "browser": {
+ "./no-ext": "./no-ext-browser.js",
+ "./ext.js": "./ext-browser.js"
+ }
+ }
+ `,
+ "/Users/user/project/src/demo-pkg/no-ext.js": `export let node = 'node'`,
+ "/Users/user/project/src/demo-pkg/no-ext-browser.js": `export let browser = 'browser'`,
+ "/Users/user/project/src/demo-pkg/ext.js": `export let node = 'node'`,
+ "/Users/user/project/src/demo-pkg/ext-browser.js": `export let browser = 'browser'`,
+ },
+ });
+ itBundled("packagejson/PackageJsonBrowserIndexNoExt", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import {browser as a} from './demo-pkg/no-ext'
+ import {node as b} from './demo-pkg/no-ext/index.js'
+ import {browser as c} from './demo-pkg/ext'
+ import {browser as d} from './demo-pkg/ext/index.js'
+ console.log(a)
+ console.log(b)
+ console.log(c)
+ console.log(d)
+ `,
+ "/Users/user/project/src/demo-pkg/package.json": /* json */ `
+ {
+ "browser": {
+ "./no-ext": "./no-ext-browser/index.js",
+ "./ext/index.js": "./ext-browser/index.js"
+ }
+ }
+ `,
+ "/Users/user/project/src/demo-pkg/no-ext/index.js": `export let node = 'node'`,
+ "/Users/user/project/src/demo-pkg/no-ext-browser/index.js": `export let browser = 'browser'`,
+ "/Users/user/project/src/demo-pkg/ext/index.js": `export let node = 'node'`,
+ "/Users/user/project/src/demo-pkg/ext-browser/index.js": `export let browser = 'browser'`,
+ },
+ });
+ itBundled("packagejson/PackageJsonBrowserIssue2002A", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": `require('pkg/sub')`,
+ "/Users/user/project/src/node_modules/pkg/package.json": /* json */ `
+ {
+ "browser": {
+ "./sub": "./sub/foo.js"
+ }
+ }
+ `,
+ "/Users/user/project/src/node_modules/pkg/sub/foo.js": `require('sub')`,
+ "/Users/user/project/src/node_modules/sub/package.json": `{ "main": "./bar" }`,
+ "/Users/user/project/src/node_modules/sub/bar.js": `works()`,
+ },
+ });
+ itBundled("packagejson/PackageJsonBrowserIssue2002B", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": `require('pkg/sub')`,
+ "/Users/user/project/src/node_modules/pkg/package.json": /* json */ `
+ {
+ "browser": {
+ "./sub": "./sub/foo.js",
+ "./sub/sub": "./sub/bar.js"
+ }
+ }
+ `,
+ "/Users/user/project/src/node_modules/pkg/sub/foo.js": `require('sub')`,
+ "/Users/user/project/src/node_modules/pkg/sub/bar.js": `works()`,
+ },
+ });
+ itBundled("packagejson/PackageJsonBrowserIssue2002C", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": `require('pkg/sub')`,
+ "/Users/user/project/src/node_modules/pkg/package.json": /* json */ `
+ {
+ "browser": {
+ "./sub": "./sub/foo.js",
+ "./sub/sub.js": "./sub/bar.js"
+ }
+ }
+ `,
+ "/Users/user/project/src/node_modules/pkg/sub/foo.js": `require('sub')`,
+ "/Users/user/project/src/node_modules/sub/index.js": `works()`,
+ },
+ });
+ itBundled("packagejson/PackageJsonDualPackageHazardImportOnly", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import value from 'demo-pkg'
+ console.log(value)
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "main": "./main.js",
+ "module": "./module.js"
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/main.js": `module.exports = 'main'`,
+ "/Users/user/project/node_modules/demo-pkg/module.js": `export default 'module'`,
+ },
+ });
+ itBundled("packagejson/PackageJsonDualPackageHazardRequireOnly", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": `console.log(require('demo-pkg'))`,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "main": "./main.js",
+ "module": "./module.js"
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/main.js": `module.exports = 'main'`,
+ "/Users/user/project/node_modules/demo-pkg/module.js": `export default 'module'`,
+ },
+ });
+ itBundled("packagejson/PackageJsonDualPackageHazardImportAndRequireSameFile", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import value from 'demo-pkg'
+ console.log(value, require('demo-pkg'))
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "main": "./main.js",
+ "module": "./module.js"
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/main.js": `module.exports = 'main'`,
+ "/Users/user/project/node_modules/demo-pkg/module.js": `export default 'module'`,
+ },
+ });
+ itBundled("packagejson/PackageJsonDualPackageHazardImportAndRequireSeparateFiles", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import './test-main'
+ import './test-module'
+ `,
+ "/Users/user/project/src/test-main.js": `console.log(require('demo-pkg'))`,
+ "/Users/user/project/src/test-module.js": /* js */ `
+ import value from 'demo-pkg'
+ console.log(value)
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "main": "./main.js",
+ "module": "./module.js"
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/main.js": `module.exports = 'main'`,
+ "/Users/user/project/node_modules/demo-pkg/module.js": `export default 'module'`,
+ },
+ });
+ itBundled("packagejson/PackageJsonDualPackageHazardImportAndRequireForceModuleBeforeMain", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import './test-main'
+ import './test-module'
+ `,
+ "/Users/user/project/src/test-main.js": `console.log(require('demo-pkg'))`,
+ "/Users/user/project/src/test-module.js": /* js */ `
+ import value from 'demo-pkg'
+ console.log(value)
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "main": "./main.js",
+ "module": "./module.js"
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/main.js": `module.exports = 'main'`,
+ "/Users/user/project/node_modules/demo-pkg/module.js": `export default 'module'`,
+ },
+ mainFields: ["module", "main"],
+ });
+ itBundled("packagejson/PackageJsonDualPackageHazardImportAndRequireImplicitMain", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import './test-index'
+ import './test-module'
+ `,
+ "/Users/user/project/src/test-index.js": `console.log(require('demo-pkg'))`,
+ "/Users/user/project/src/test-module.js": /* js */ `
+ import value from 'demo-pkg'
+ console.log(value)
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "module": "./module.js"
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/index.js": `module.exports = 'index'`,
+ "/Users/user/project/node_modules/demo-pkg/module.js": `export default 'module'`,
+ },
+ });
+ itBundled("packagejson/PackageJsonDualPackageHazardImportAndRequireImplicitMainForceModuleBeforeMain", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import './test-index'
+ import './test-module'
+ `,
+ "/Users/user/project/src/test-index.js": `console.log(require('demo-pkg'))`,
+ "/Users/user/project/src/test-module.js": /* js */ `
+ import value from 'demo-pkg'
+ console.log(value)
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "module": "./module.js"
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/index.js": `module.exports = 'index'`,
+ "/Users/user/project/node_modules/demo-pkg/module.js": `export default 'module'`,
+ },
+ mainFields: ["module", "main"],
+ });
+ itBundled("packagejson/PackageJsonDualPackageHazardImportAndRequireBrowser", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import './test-main'
+ import './test-module'
+ `,
+ "/Users/user/project/src/test-main.js": `console.log(require('demo-pkg'))`,
+ "/Users/user/project/src/test-module.js": /* js */ `
+ import value from 'demo-pkg'
+ console.log(value)
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "main": "./main.js",
+ "module": "./module.js",
+ "browser": {
+ "./main.js": "./main.browser.js",
+ "./module.js": "./module.browser.js"
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/main.js": `module.exports = 'main'`,
+ "/Users/user/project/node_modules/demo-pkg/module.js": `export default 'module'`,
+ "/Users/user/project/node_modules/demo-pkg/main.browser.js": `module.exports = 'browser main'`,
+ "/Users/user/project/node_modules/demo-pkg/module.browser.js": `export default 'browser module'`,
+ },
+ });
+ itBundled("packagejson/PackageJsonMainFieldsA", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import value from 'demo-pkg'
+ console.log(value)
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "a": "./a.js",
+ "b": "./b.js"
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/a.js": `module.exports = 'a'`,
+ "/Users/user/project/node_modules/demo-pkg/b.js": `export default 'b'`,
+ },
+ mainFields: ["a", "b"],
+ });
+ itBundled("packagejson/PackageJsonMainFieldsB", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import value from 'demo-pkg'
+ console.log(value)
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "a": "./a.js",
+ "b": "./b.js"
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/a.js": `module.exports = 'a'`,
+ "/Users/user/project/node_modules/demo-pkg/b.js": `export default 'b'`,
+ },
+ mainFields: ["b", "a"],
+ });
+ itBundled("packagejson/PackageJsonNeutralNoDefaultMainFields", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import fn from 'demo-pkg'
+ console.log(fn())
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "main": "./main.js",
+ "module": "./main.esm.js"
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/main.js": /* js */ `
+ module.exports = function() {
+ return 123
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/main.esm.js": /* js */ `
+ export default function() {
+ return 123
+ }
+ `,
+ },
+ platform: "neutral",
+ /* TODO FIX expectedScanLog: `Users/user/project/src/entry.js: ERROR: Could not resolve "demo-pkg"
+ Users/user/project/node_modules/demo-pkg/package.json: NOTE: The "main" field here was ignored. Main fields must be configured explicitly when using the "neutral" platform.
+ NOTE: You can mark the path "demo-pkg" as external to exclude it from the bundle, which will remove this error.
+ `, */
+ });
+ itBundled("packagejson/PackageJsonNeutralExplicitMainFields", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import fn from 'demo-pkg'
+ console.log(fn())
+ `,
+ "/Users/user/project/node_modules/demo-pkg/package.json": /* json */ `
+ {
+ "hello": "./main.js",
+ "module": "./main.esm.js"
+ }
+ `,
+ "/Users/user/project/node_modules/demo-pkg/main.js": /* js */ `
+ module.exports = function() {
+ return 123
+ }
+ `,
+ },
+ platform: "neutral",
+ mainFields: ["hello"],
+ });
+ itBundled("packagejson/PackageJsonExportsErrorInvalidModuleSpecifier", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import 'pkg1'
+ import 'pkg2'
+ import 'pkg3'
+ import 'pkg4'
+ import 'pkg5'
+ import 'pkg6'
+ `,
+ "/Users/user/project/node_modules/pkg1/package.json": `{ "exports": { ".": "./%%" } }`,
+ "/Users/user/project/node_modules/pkg2/package.json": `{ "exports": { ".": "./%2f" } }`,
+ "/Users/user/project/node_modules/pkg3/package.json": `{ "exports": { ".": "./%2F" } }`,
+ "/Users/user/project/node_modules/pkg4/package.json": `{ "exports": { ".": "./%5c" } }`,
+ "/Users/user/project/node_modules/pkg5/package.json": `{ "exports": { ".": "./%5C" } }`,
+ "/Users/user/project/node_modules/pkg6/package.json": `{ "exports": { ".": "./%31.js" } }`,
+ "/Users/user/project/node_modules/pkg6/1.js": `console.log(1)`,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/src/entry.js: ERROR: Could not resolve "pkg1"
+ Users/user/project/node_modules/pkg1/package.json: NOTE: The module specifier "./%%" is invalid:
+ NOTE: You can mark the path "pkg1" as external to exclude it from the bundle, which will remove this error.
+ Users/user/project/src/entry.js: ERROR: Could not resolve "pkg2"
+ Users/user/project/node_modules/pkg2/package.json: NOTE: The module specifier "./%2f" is invalid:
+ NOTE: You can mark the path "pkg2" as external to exclude it from the bundle, which will remove this error.
+ Users/user/project/src/entry.js: ERROR: Could not resolve "pkg3"
+ Users/user/project/node_modules/pkg3/package.json: NOTE: The module specifier "./%2F" is invalid:
+ NOTE: You can mark the path "pkg3" as external to exclude it from the bundle, which will remove this error.
+ Users/user/project/src/entry.js: ERROR: Could not resolve "pkg4"
+ Users/user/project/node_modules/pkg4/package.json: NOTE: The module specifier "./%5c" is invalid:
+ NOTE: You can mark the path "pkg4" as external to exclude it from the bundle, which will remove this error.
+ Users/user/project/src/entry.js: ERROR: Could not resolve "pkg5"
+ Users/user/project/node_modules/pkg5/package.json: NOTE: The module specifier "./%5C" is invalid:
+ NOTE: You can mark the path "pkg5" as external to exclude it from the bundle, which will remove this error.
+ `, */
+ });
+ itBundled("packagejson/PackageJsonExportsErrorInvalidPackageConfiguration", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import 'pkg1'
+ import 'pkg2/foo'
+ `,
+ "/Users/user/project/node_modules/pkg1/package.json": `{ "exports": { ".": false } }`,
+ "/Users/user/project/node_modules/pkg2/package.json": `{ "exports": { "./foo": false } }`,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/node_modules/pkg1/package.json: WARNING: This value must be a string, an object, an array, or null
+ Users/user/project/node_modules/pkg2/package.json: WARNING: This value must be a string, an object, an array, or null
+ Users/user/project/src/entry.js: ERROR: Could not resolve "pkg1"
+ Users/user/project/node_modules/pkg1/package.json: NOTE: The package configuration has an invalid value here:
+ NOTE: You can mark the path "pkg1" as external to exclude it from the bundle, which will remove this error.
+ Users/user/project/src/entry.js: ERROR: Could not resolve "pkg2/foo"
+ Users/user/project/node_modules/pkg2/package.json: NOTE: The package configuration has an invalid value here:
+ NOTE: You can mark the path "pkg2/foo" as external to exclude it from the bundle, which will remove this error.
+ `, */
+ });
+ itBundled("packagejson/PackageJsonExportsErrorInvalidPackageTarget", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import 'pkg1'
+ import 'pkg2'
+ import 'pkg3'
+ `,
+ "/Users/user/project/node_modules/pkg1/package.json": `{ "exports": { ".": "invalid" } }`,
+ "/Users/user/project/node_modules/pkg2/package.json": `{ "exports": { ".": "./../pkg3" } }`,
+ "/Users/user/project/node_modules/pkg3/package.json": `{ "exports": { ".": "./node_modules/pkg" } }`,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/src/entry.js: ERROR: Could not resolve "pkg1"
+ Users/user/project/node_modules/pkg1/package.json: NOTE: The package target "invalid" is invalid because it doesn't start with "./":
+ NOTE: You can mark the path "pkg1" as external to exclude it from the bundle, which will remove this error.
+ Users/user/project/src/entry.js: ERROR: Could not resolve "pkg2"
+ Users/user/project/node_modules/pkg2/package.json: NOTE: The package target "./../pkg3" is invalid because it contains invalid segment "..":
+ NOTE: You can mark the path "pkg2" as external to exclude it from the bundle, which will remove this error.
+ Users/user/project/src/entry.js: ERROR: Could not resolve "pkg3"
+ Users/user/project/node_modules/pkg3/package.json: NOTE: The package target "./node_modules/pkg" is invalid because it contains invalid segment "node_modules":
+ NOTE: You can mark the path "pkg3" as external to exclude it from the bundle, which will remove this error.
+ `, */
+ });
+ itBundled("packagejson/PackageJsonExportsErrorPackagePathNotExported", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": `import 'pkg1/foo'`,
+ "/Users/user/project/node_modules/pkg1/package.json": `{ "exports": { ".": {} } }`,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/src/entry.js: ERROR: Could not resolve "pkg1/foo"
+ Users/user/project/node_modules/pkg1/package.json: NOTE: The path "./foo" is not exported by package "pkg1":
+ NOTE: You can mark the path "pkg1/foo" as external to exclude it from the bundle, which will remove this error.
+ `, */
+ });
+ itBundled("packagejson/PackageJsonExportsErrorModuleNotFound", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": `import 'pkg1'`,
+ "/Users/user/project/node_modules/pkg1/package.json": `{ "exports": { ".": "./foo.js" } }`,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/src/entry.js: ERROR: Could not resolve "pkg1"
+ Users/user/project/node_modules/pkg1/package.json: NOTE: The module "./foo.js" was not found on the file system:
+ NOTE: You can mark the path "pkg1" as external to exclude it from the bundle, which will remove this error.
+ `, */
+ });
+ itBundled("packagejson/PackageJsonExportsErrorUnsupportedDirectoryImport", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import 'pkg1'
+ import 'pkg2'
+ `,
+ "/Users/user/project/node_modules/pkg1/package.json": `{ "exports": { ".": "./foo/" } }`,
+ "/Users/user/project/node_modules/pkg2/package.json": `{ "exports": { ".": "./foo" } }`,
+ "/Users/user/project/node_modules/pkg2/foo/bar.js": `console.log(bar)`,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/src/entry.js: ERROR: Could not resolve "pkg1"
+ Users/user/project/node_modules/pkg1/package.json: NOTE: The module "./foo" was not found on the file system:
+ NOTE: You can mark the path "pkg1" as external to exclude it from the bundle, which will remove this error.
+ Users/user/project/src/entry.js: ERROR: Could not resolve "pkg2"
+ Users/user/project/node_modules/pkg2/package.json: NOTE: Importing the directory "./foo" is forbidden by this package:
+ Users/user/project/node_modules/pkg2/package.json: NOTE: The presence of "exports" here makes importing a directory forbidden:
+ NOTE: You can mark the path "pkg2" as external to exclude it from the bundle, which will remove this error.
+ `, */
+ });
+ itBundled("packagejson/PackageJsonExportsRequireOverImport", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": `require('pkg')`,
+ "/Users/user/project/node_modules/pkg/package.json": /* json */ `
+ {
+ "exports": {
+ "import": "./import.js",
+ "require": "./require.js",
+ "default": "./default.js"
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/pkg/import.js": `console.log('FAILURE')`,
+ "/Users/user/project/node_modules/pkg/require.js": `console.log('SUCCESS')`,
+ },
+ });
+ itBundled("packagejson/PackageJsonExportsImportOverRequire", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": `import 'pkg'`,
+ "/Users/user/project/node_modules/pkg/package.json": /* json */ `
+ {
+ "exports": {
+ "require": "./require.js",
+ "import": "./import.js",
+ "default": "./default.js"
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/pkg/require.js": `console.log('FAILURE')`,
+ "/Users/user/project/node_modules/pkg/import.js": `console.log('SUCCESS')`,
+ },
+ });
+ itBundled("packagejson/PackageJsonExportsDefaultOverImportAndRequire", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": `import 'pkg'`,
+ "/Users/user/project/node_modules/pkg/package.json": /* json */ `
+ {
+ "exports": {
+ "default": "./default.js",
+ "import": "./import.js",
+ "require": "./require.js"
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/pkg/require.js": `console.log('FAILURE')`,
+ "/Users/user/project/node_modules/pkg/import.js": `console.log('FAILURE')`,
+ "/Users/user/project/node_modules/pkg/default.js": `console.log('SUCCESS')`,
+ },
+ });
+ itBundled("packagejson/PackageJsonExportsEntryPointImportOverRequire", {
+ // GENERATED
+ files: {
+ "/node_modules/pkg/package.json": /* json */ `
+ {
+ "exports": {
+ "import": "./import.js",
+ "require": "./require.js"
+ },
+ "module": "./module.js",
+ "main": "./main.js"
+ }
+ `,
+ "/node_modules/pkg/import.js": `console.log('SUCCESS')`,
+ "/node_modules/pkg/require.js": `console.log('FAILURE')`,
+ "/node_modules/pkg/module.js": `console.log('FAILURE')`,
+ "/node_modules/pkg/main.js": `console.log('FAILURE')`,
+ },
+ entryPoints: ["pkg"],
+ });
+ itBundled("packagejson/PackageJsonExportsEntryPointRequireOnly", {
+ // GENERATED
+ files: {
+ "/node_modules/pkg/package.json": /* json */ `
+ {
+ "exports": {
+ "require": "./require.js"
+ },
+ "module": "./module.js",
+ "main": "./main.js"
+ }
+ `,
+ "/node_modules/pkg/require.js": `console.log('FAILURE')`,
+ "/node_modules/pkg/module.js": `console.log('FAILURE')`,
+ "/node_modules/pkg/main.js": `console.log('FAILURE')`,
+ },
+ entryPoints: ["pkg"],
+ /* TODO FIX expectedScanLog: `ERROR: Could not resolve "pkg"
+ node_modules/pkg/package.json: NOTE: The path "." is not currently exported by package "pkg":
+ node_modules/pkg/package.json: NOTE: None of the conditions provided ("require") match any of the currently active conditions ("browser", "default", "import"):
+ `, */
+ });
+ itBundled("packagejson/PackageJsonExportsEntryPointModuleOverMain", {
+ // GENERATED
+ files: {
+ "/node_modules/pkg/package.json": /* json */ `
+ {
+ "module": "./module.js",
+ "main": "./main.js"
+ }
+ `,
+ "/node_modules/pkg/module.js": `console.log('SUCCESS')`,
+ "/node_modules/pkg/main.js": `console.log('FAILURE')`,
+ },
+ entryPoints: ["pkg"],
+ });
+ itBundled("packagejson/PackageJsonExportsEntryPointMainOnly", {
+ // GENERATED
+ files: {
+ "/node_modules/pkg/package.json": /* json */ `
+ {
+ "main": "./main.js"
+ }
+ `,
+ "/node_modules/pkg/main.js": `console.log('SUCCESS')`,
+ },
+ entryPoints: ["pkg"],
+ });
+ itBundled("packagejson/PackageJsonExportsBrowser", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": `import 'pkg'`,
+ "/Users/user/project/node_modules/pkg/package.json": /* json */ `
+ {
+ "exports": {
+ "node": "./node.js",
+ "browser": "./browser.js",
+ "default": "./default.js"
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/pkg/node.js": `console.log('FAILURE')`,
+ "/Users/user/project/node_modules/pkg/browser.js": `console.log('SUCCESS')`,
+ },
+ outfile: "/Users/user/project/out.js",
+ });
+ itBundled("packagejson/PackageJsonExportsNode", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": `import 'pkg'`,
+ "/Users/user/project/node_modules/pkg/package.json": /* json */ `
+ {
+ "exports": {
+ "browser": "./browser.js",
+ "node": "./node.js",
+ "default": "./default.js"
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/pkg/browser.js": `console.log('FAILURE')`,
+ "/Users/user/project/node_modules/pkg/node.js": `console.log('SUCCESS')`,
+ },
+ outfile: "/Users/user/project/out.js",
+ });
+ itBundled("packagejson/PackageJsonExportsNeutral", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": `import 'pkg'`,
+ "/Users/user/project/node_modules/pkg/package.json": /* json */ `
+ {
+ "exports": {
+ "node": "./node.js",
+ "browser": "./browser.js",
+ "default": "./default.js"
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/pkg/node.js": `console.log('FAILURE')`,
+ "/Users/user/project/node_modules/pkg/browser.js": `console.log('FAILURE')`,
+ "/Users/user/project/node_modules/pkg/default.js": `console.log('SUCCESS')`,
+ },
+ outfile: "/Users/user/project/out.js",
+ });
+ itBundled("packagejson/PackageJsonExportsOrderIndependent", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import 'pkg1/foo/bar.js'
+ import 'pkg2/foo/bar.js'
+ `,
+ "/Users/user/project/node_modules/pkg1/package.json": /* json */ `
+ {
+ "exports": {
+ "./": "./1/",
+ "./foo/": "./2/"
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/pkg1/1/foo/bar.js": `console.log('FAILURE')`,
+ "/Users/user/project/node_modules/pkg1/2/bar.js": `console.log('SUCCESS')`,
+ "/Users/user/project/node_modules/pkg2/package.json": /* json */ `
+ {
+ "exports": {
+ "./foo/": "./1/",
+ "./": "./2/"
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/pkg2/1/bar.js": `console.log('SUCCESS')`,
+ "/Users/user/project/node_modules/pkg2/2/foo/bar.js": `console.log('FAILURE')`,
+ },
+ });
+ itBundled("packagejson/PackageJsonExportsWildcard", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import 'pkg1/foo'
+ import 'pkg1/foo2'
+ `,
+ "/Users/user/project/node_modules/pkg1/package.json": /* json */ `
+ {
+ "exports": {
+ "./foo*": "./file*.js"
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/pkg1/file.js": `console.log('SUCCESS')`,
+ "/Users/user/project/node_modules/pkg1/file2.js": `console.log('SUCCESS')`,
+ },
+ });
+ itBundled("packagejson/PackageJsonExportsErrorMissingTrailingSlash", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": `import 'pkg1/foo/bar'`,
+ "/Users/user/project/node_modules/pkg1/package.json": `{ "exports": { "./foo/": "./test" } }`,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/src/entry.js: ERROR: Could not resolve "pkg1/foo/bar"
+ Users/user/project/node_modules/pkg1/package.json: NOTE: The module specifier "./test" is invalid because it doesn't end in "/":
+ NOTE: You can mark the path "pkg1/foo/bar" as external to exclude it from the bundle, which will remove this error.
+ `, */
+ });
+ itBundled("packagejson/PackageJsonExportsCustomConditions", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": `import 'pkg1'`,
+ "/Users/user/project/node_modules/pkg1/package.json": /* json */ `
+ {
+ "exports": {
+ "custom1": "./custom1.js",
+ "custom2": "./custom2.js",
+ "default": "./default.js"
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/pkg1/custom2.js": `console.log('SUCCESS')`,
+ },
+ outfile: "/Users/user/project/out.js",
+ });
+ itBundled("packagejson/PackageJsonExportsNotExactMissingExtension", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": `import 'pkg1/foo/bar'`,
+ "/Users/user/project/node_modules/pkg1/package.json": /* json */ `
+ {
+ "exports": {
+ "./foo/": "./dir/"
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/pkg1/dir/bar.js": `console.log('SUCCESS')`,
+ },
+ });
+ itBundled("packagejson/PackageJsonExportsNotExactMissingExtensionPattern", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": `import 'pkg1/foo/bar'`,
+ "/Users/user/project/node_modules/pkg1/package.json": /* json */ `
+ {
+ "exports": {
+ "./foo/*": "./dir/*"
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/pkg1/dir/bar.js": `console.log('SUCCESS')`,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/src/entry.js: ERROR: Could not resolve "pkg1/foo/bar"
+ Users/user/project/node_modules/pkg1/package.json: NOTE: The module "./dir/bar" was not found on the file system:
+ Users/user/project/src/entry.js: NOTE: Import from "pkg1/foo/bar.js" to get the file "Users/user/project/node_modules/pkg1/dir/bar.js":
+ NOTE: You can mark the path "pkg1/foo/bar" as external to exclude it from the bundle, which will remove this error.
+ `, */
+ });
+ itBundled("packagejson/PackageJsonExportsExactMissingExtension", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": `import 'pkg1/foo/bar'`,
+ "/Users/user/project/node_modules/pkg1/package.json": /* json */ `
+ {
+ "exports": {
+ "./foo/bar": "./dir/bar"
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/pkg1/dir/bar.js": `console.log('SUCCESS')`,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/src/entry.js: ERROR: Could not resolve "pkg1/foo/bar"
+ Users/user/project/node_modules/pkg1/package.json: NOTE: The module "./dir/bar" was not found on the file system:
+ NOTE: You can mark the path "pkg1/foo/bar" as external to exclude it from the bundle, which will remove this error.
+ `, */
+ });
+ itBundled("packagejson/PackageJsonExportsNoConditionsMatch", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import 'pkg1'
+ import 'pkg1/foo.js'
+ `,
+ "/Users/user/project/node_modules/pkg1/package.json": /* json */ `
+ {
+ "exports": {
+ ".": {
+ "what": "./foo.js"
+ },
+ "./foo.js": {
+ "what": "./foo.js"
+ }
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/pkg1/foo.js": `console.log('FAILURE')`,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/src/entry.js: ERROR: Could not resolve "pkg1"
+ Users/user/project/node_modules/pkg1/package.json: NOTE: The path "." is not currently exported by package "pkg1":
+ Users/user/project/node_modules/pkg1/package.json: NOTE: None of the conditions provided ("what") match any of the currently active conditions ("browser", "default", "import"):
+ Users/user/project/node_modules/pkg1/package.json: NOTE: Consider enabling the "what" condition if this package expects it to be enabled. You can use 'Conditions: []string{"what"}' to do that:
+ NOTE: You can mark the path "pkg1" as external to exclude it from the bundle, which will remove this error.
+ Users/user/project/src/entry.js: ERROR: Could not resolve "pkg1/foo.js"
+ Users/user/project/node_modules/pkg1/package.json: NOTE: The path "./foo.js" is not currently exported by package "pkg1":
+ Users/user/project/node_modules/pkg1/package.json: NOTE: None of the conditions provided ("what") match any of the currently active conditions ("browser", "default", "import"):
+ Users/user/project/node_modules/pkg1/package.json: NOTE: Consider enabling the "what" condition if this package expects it to be enabled. You can use 'Conditions: []string{"what"}' to do that:
+ NOTE: You can mark the path "pkg1/foo.js" as external to exclude it from the bundle, which will remove this error.
+ `, */
+ });
+ itBundled("packagejson/PackageJsonExportsMustUseRequire", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import 'pkg1'
+ import 'pkg1/foo.js'
+ `,
+ "/Users/user/project/node_modules/pkg1/package.json": /* json */ `
+ {
+ "exports": {
+ ".": {
+ "require": "./foo.js"
+ },
+ "./foo.js": {
+ "require": "./foo.js"
+ }
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/pkg1/foo.js": `console.log('FAILURE')`,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/src/entry.js: ERROR: Could not resolve "pkg1"
+ Users/user/project/node_modules/pkg1/package.json: NOTE: The path "." is not currently exported by package "pkg1":
+ Users/user/project/node_modules/pkg1/package.json: NOTE: None of the conditions provided ("require") match any of the currently active conditions ("browser", "default", "import"):
+ Users/user/project/src/entry.js: NOTE: Consider using a "require()" call to import this file, which will work because the "require" condition is supported by this package:
+ NOTE: You can mark the path "pkg1" as external to exclude it from the bundle, which will remove this error.
+ Users/user/project/src/entry.js: ERROR: Could not resolve "pkg1/foo.js"
+ Users/user/project/node_modules/pkg1/package.json: NOTE: The path "./foo.js" is not currently exported by package "pkg1":
+ Users/user/project/node_modules/pkg1/package.json: NOTE: None of the conditions provided ("require") match any of the currently active conditions ("browser", "default", "import"):
+ Users/user/project/src/entry.js: NOTE: Consider using a "require()" call to import this file, which will work because the "require" condition is supported by this package:
+ NOTE: You can mark the path "pkg1/foo.js" as external to exclude it from the bundle, which will remove this error.
+ `, */
+ });
+ itBundled("packagejson/PackageJsonExportsMustUseImport", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ require('pkg1')
+ require('pkg1/foo.js')
+ `,
+ "/Users/user/project/node_modules/pkg1/package.json": /* json */ `
+ {
+ "exports": {
+ ".": {
+ "import": "./foo.js"
+ },
+ "./foo.js": {
+ "import": "./foo.js"
+ }
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/pkg1/foo.js": `console.log('FAILURE')`,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/src/entry.js: ERROR: Could not resolve "pkg1"
+ Users/user/project/node_modules/pkg1/package.json: NOTE: The path "." is not currently exported by package "pkg1":
+ Users/user/project/node_modules/pkg1/package.json: NOTE: None of the conditions provided ("import") match any of the currently active conditions ("browser", "default", "require"):
+ Users/user/project/src/entry.js: NOTE: Consider using an "import" statement to import this file, which will work because the "import" condition is supported by this package:
+ NOTE: You can mark the path "pkg1" as external to exclude it from the bundle, which will remove this error. You can also surround this "require" call with a try/catch block to handle this failure at run-time instead of bundle-time.
+ Users/user/project/src/entry.js: ERROR: Could not resolve "pkg1/foo.js"
+ Users/user/project/node_modules/pkg1/package.json: NOTE: The path "./foo.js" is not currently exported by package "pkg1":
+ Users/user/project/node_modules/pkg1/package.json: NOTE: None of the conditions provided ("import") match any of the currently active conditions ("browser", "default", "require"):
+ Users/user/project/src/entry.js: NOTE: Consider using an "import" statement to import this file, which will work because the "import" condition is supported by this package:
+ NOTE: You can mark the path "pkg1/foo.js" as external to exclude it from the bundle, which will remove this error. You can also surround this "require" call with a try/catch block to handle this failure at run-time instead of bundle-time.
+ `, */
+ });
+ itBundled("packagejson/PackageJsonExportsReverseLookup", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ require('pkg/path/to/real/file')
+ require('pkg/path/to/other/file')
+ `,
+ "/Users/user/project/node_modules/pkg/package.json": /* json */ `
+ {
+ "exports": {
+ "./lib/te*": {
+ "default": "./path/to/re*.js"
+ },
+ "./extra/": {
+ "default": "./path/to/"
+ }
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/pkg/path/to/real/file.js": ``,
+ "/Users/user/project/node_modules/pkg/path/to/other/file.js": ``,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/src/entry.js: ERROR: Could not resolve "pkg/path/to/real/file"
+ Users/user/project/node_modules/pkg/package.json: NOTE: The path "./path/to/real/file" is not exported by package "pkg":
+ Users/user/project/node_modules/pkg/package.json: NOTE: The file "./path/to/real/file.js" is exported at path "./lib/teal/file":
+ Users/user/project/src/entry.js: NOTE: Import from "pkg/lib/teal/file" to get the file "Users/user/project/node_modules/pkg/path/to/real/file.js":
+ NOTE: You can mark the path "pkg/path/to/real/file" as external to exclude it from the bundle, which will remove this error. You can also surround this "require" call with a try/catch block to handle this failure at run-time instead of bundle-time.
+ Users/user/project/src/entry.js: ERROR: Could not resolve "pkg/path/to/other/file"
+ Users/user/project/node_modules/pkg/package.json: NOTE: The path "./path/to/other/file" is not exported by package "pkg":
+ Users/user/project/node_modules/pkg/package.json: NOTE: The file "./path/to/other/file.js" is exported at path "./extra/other/file.js":
+ Users/user/project/src/entry.js: NOTE: Import from "pkg/extra/other/file.js" to get the file "Users/user/project/node_modules/pkg/path/to/other/file.js":
+ NOTE: You can mark the path "pkg/path/to/other/file" as external to exclude it from the bundle, which will remove this error. You can also surround this "require" call with a try/catch block to handle this failure at run-time instead of bundle-time.
+ `, */
+ });
+ itBundled("packagejson/PackageJsonExportsPatternTrailers", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import 'pkg/path/foo.js/bar.js'
+ import 'pkg2/features/abc'
+ import 'pkg2/features/xyz.js'
+ `,
+ "/Users/user/project/node_modules/pkg/package.json": /* json */ `
+ {
+ "exports": {
+ "./path/*/bar.js": "./dir/baz-*"
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/pkg/dir/baz-foo.js": `console.log('works')`,
+ "/Users/user/project/node_modules/pkg2/package.json": /* json */ `
+ {
+ "exports": {
+ "./features/*": "./public/*.js",
+ "./features/*.js": "./public/*.js"
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/pkg2/public/abc.js": `console.log('abc')`,
+ "/Users/user/project/node_modules/pkg2/public/xyz.js": `console.log('xyz')`,
+ },
+ });
+ itBundled("packagejson/PackageJsonExportsAlternatives", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import redApple from 'pkg/apples/red.js'
+ import greenApple from 'pkg/apples/green.js'
+ import redBook from 'pkg/books/red'
+ import greenBook from 'pkg/books/green'
+ console.log({redApple, greenApple, redBook, greenBook})
+ `,
+ "/Users/user/project/node_modules/pkg/package.json": /* json */ `
+ {
+ "exports": {
+ "./apples/": ["./good-apples/", "./bad-apples/"],
+ "./books/*": ["./good-books/*-book.js", "./bad-books/*-book.js"]
+ }
+ }
+ `,
+ "/Users/user/project/node_modules/pkg/good-apples/green.js": `export default '🍏'`,
+ "/Users/user/project/node_modules/pkg/bad-apples/red.js": `export default '🍎'`,
+ "/Users/user/project/node_modules/pkg/good-books/green-book.js": `export default '📗'`,
+ "/Users/user/project/node_modules/pkg/bad-books/red-book.js": `export default '📕'`,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/src/entry.js: ERROR: Could not resolve "pkg/apples/red.js"
+ Users/user/project/node_modules/pkg/package.json: NOTE: The module "./good-apples/red.js" was not found on the file system:
+ NOTE: You can mark the path "pkg/apples/red.js" as external to exclude it from the bundle, which will remove this error.
+ Users/user/project/src/entry.js: ERROR: Could not resolve "pkg/books/red"
+ Users/user/project/node_modules/pkg/package.json: NOTE: The module "./good-books/red-book.js" was not found on the file system:
+ NOTE: You can mark the path "pkg/books/red" as external to exclude it from the bundle, which will remove this error.
+ `, */
+ });
+ itBundled("packagejson/PackageJsonImports", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/foo/entry.js": /* js */ `
+ import '#top-level'
+ import '#nested/path.js'
+ import '#star/c.js'
+ import '#slash/d.js'
+ `,
+ "/Users/user/project/src/package.json": /* json */ `
+ {
+ "imports": {
+ "#top-level": "./a.js",
+ "#nested/path.js": "./b.js",
+ "#star/*": "./some-star/*",
+ "#slash/": "./some-slash/"
+ }
+ }
+ `,
+ "/Users/user/project/src/a.js": `console.log('a.js')`,
+ "/Users/user/project/src/b.js": `console.log('b.js')`,
+ "/Users/user/project/src/some-star/c.js": `console.log('c.js')`,
+ "/Users/user/project/src/some-slash/d.js": `console.log('d.js')`,
+ },
+ });
+ itBundled("packagejson/PackageJsonImportsRemapToOtherPackage", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": /* js */ `
+ import '#top-level'
+ import '#nested/path.js'
+ import '#star/c.js'
+ import '#slash/d.js'
+ `,
+ "/Users/user/project/src/package.json": /* json */ `
+ {
+ "imports": {
+ "#top-level": "pkg/a.js",
+ "#nested/path.js": "pkg/b.js",
+ "#star/*": "pkg/some-star/*",
+ "#slash/": "pkg/some-slash/"
+ }
+ }
+ `,
+ "/Users/user/project/src/node_modules/pkg/a.js": `console.log('a.js')`,
+ "/Users/user/project/src/node_modules/pkg/b.js": `console.log('b.js')`,
+ "/Users/user/project/src/node_modules/pkg/some-star/c.js": `console.log('c.js')`,
+ "/Users/user/project/src/node_modules/pkg/some-slash/d.js": `console.log('d.js')`,
+ },
+ });
+ itBundled("packagejson/PackageJsonImportsErrorMissingRemappedPackage", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": `import '#foo'`,
+ "/Users/user/project/src/package.json": /* json */ `
+ {
+ "imports": {
+ "#foo": "bar"
+ }
+ }
+ `,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/src/entry.js: ERROR: Could not resolve "#foo"
+ Users/user/project/src/package.json: NOTE: The remapped path "bar" could not be resolved:
+ NOTE: You can mark the path "#foo" as external to exclude it from the bundle, which will remove this error.
+ `, */
+ });
+ itBundled("packagejson/PackageJsonImportsInvalidPackageConfiguration", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": `import '#foo'`,
+ "/Users/user/project/src/package.json": /* json */ `
+ {
+ "imports": "#foo"
+ }
+ `,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/src/entry.js: ERROR: Could not resolve "#foo"
+ Users/user/project/src/package.json: NOTE: The package configuration has an invalid value here:
+ NOTE: You can mark the path "#foo" as external to exclude it from the bundle, which will remove this error.
+ Users/user/project/src/package.json: WARNING: The value for "imports" must be an object
+ `, */
+ });
+ itBundled("packagejson/PackageJsonImportsErrorEqualsHash", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": `import '#'`,
+ "/Users/user/project/src/package.json": /* json */ `
+ {
+ "imports": {}
+ }
+ `,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/src/entry.js: ERROR: Could not resolve "#"
+ Users/user/project/src/package.json: NOTE: This "imports" map was ignored because the module specifier "#" is invalid:
+ NOTE: You can mark the path "#" as external to exclude it from the bundle, which will remove this error.
+ `, */
+ });
+ itBundled("packagejson/PackageJsonImportsErrorStartsWithHashSlash", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": `import '#/foo'`,
+ "/Users/user/project/src/package.json": /* json */ `
+ {
+ "imports": {}
+ }
+ `,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/src/entry.js: ERROR: Could not resolve "#/foo"
+ Users/user/project/src/package.json: NOTE: This "imports" map was ignored because the module specifier "#/foo" is invalid:
+ NOTE: You can mark the path "#/foo" as external to exclude it from the bundle, which will remove this error.
+ `, */
+ });
+ itBundled("packagejson/PackageJsonMainFieldsErrorMessageDefault", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": `import 'foo'`,
+ "/Users/user/project/node_modules/foo/package.json": /* json */ `
+ {
+ "main": "./foo"
+ }
+ `,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/src/entry.js: ERROR: Could not resolve "foo"
+ NOTE: You can mark the path "foo" as external to exclude it from the bundle, which will remove this error.
+ `, */
+ });
+ itBundled("packagejson/PackageJsonMainFieldsErrorMessageNotIncluded", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": `import 'foo'`,
+ "/Users/user/project/node_modules/foo/package.json": /* json */ `
+ {
+ "main": "./foo"
+ }
+ `,
+ },
+ outfile: "/Users/user/project/out.js",
+ /* TODO FIX expectedScanLog: `Users/user/project/src/entry.js: ERROR: Could not resolve "foo"
+ Users/user/project/node_modules/foo/package.json: NOTE: The "main" field here was ignored because the list of main fields to use is currently set to ["some", "fields"].
+ NOTE: You can mark the path "foo" as external to exclude it from the bundle, which will remove this error.
+ `, */
+ });
+ itBundled("packagejson/PackageJsonMainFieldsErrorMessageEmpty", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.js": `import 'foo'`,
+ "/Users/user/project/node_modules/foo/package.json": /* json */ `
+ {
+ "main": "./foo"
+ }
+ `,
+ },
+ outfile: "/Users/user/project/out.js",
+ /* TODO FIX expectedScanLog: `Users/user/project/src/entry.js: ERROR: Could not resolve "foo"
+ Users/user/project/node_modules/foo/package.json: NOTE: The "main" field here was ignored because the list of main fields to use is currently set to [].
+ NOTE: You can mark the path "foo" as external to exclude it from the bundle, which will remove this error.
+ `, */
+ });
+ itBundled("packagejson/PackageJsonTypeShouldBeTypes", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/index.js": ``,
+ "/Users/user/project/package.json": /* json */ `
+ {
+ "main": "./src/index.js",
+ "type": "./src/index.d.ts"
+ }
+ `,
+ },
+ outfile: "/Users/user/project/out.js",
+ /* TODO FIX expectedScanLog: `Users/user/project/package.json: WARNING: "./src/index.d.ts" is not a valid value for the "type" field
+ Users/user/project/package.json: NOTE: TypeScript type declarations use the "types" field, not the "type" field:
+ `, */
+ });
+ itBundled("packagejson/PackageJsonImportSelfUsingRequire", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/index.js": /* js */ `
+ module.exports = 'index'
+ console.log(
+ require("xyz"),
+ require("xyz/bar"),
+ )
+ `,
+ "/Users/user/project/src/foo-import.js": `export default 'foo'`,
+ "/Users/user/project/src/foo-require.js": `module.exports = 'foo'`,
+ "/Users/user/project/package.json": /* json */ `
+ {
+ "name": "xyz",
+ "exports": {
+ ".": "./src/index.js",
+ "./bar": {
+ "import": "./src/foo-import.js",
+ "require": "./src/foo-require.js"
+ }
+ }
+ }
+ `,
+ },
+ outfile: "/Users/user/project/out.js",
+ });
+ itBundled("packagejson/PackageJsonImportSelfUsingImport", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/index.js": /* js */ `
+ import xyz from "xyz"
+ import foo from "xyz/bar"
+ export default 'index'
+ console.log(xyz, foo)
+ `,
+ "/Users/user/project/src/foo-import.js": `export default 'foo'`,
+ "/Users/user/project/src/foo-require.js": `module.exports = 'foo'`,
+ "/Users/user/project/package.json": /* json */ `
+ {
+ "name": "xyz",
+ "exports": {
+ ".": "./src/index.js",
+ "./bar": {
+ "import": "./src/foo-import.js",
+ "require": "./src/foo-require.js"
+ }
+ }
+ }
+ `,
+ },
+ outfile: "/Users/user/project/out.js",
+ });
+ itBundled("packagejson/PackageJsonImportSelfUsingRequireScoped", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/index.js": /* js */ `
+ module.exports = 'index'
+ console.log(
+ require("@some-scope/xyz"),
+ require("@some-scope/xyz/bar"),
+ )
+ `,
+ "/Users/user/project/src/foo-import.js": `export default 'foo'`,
+ "/Users/user/project/src/foo-require.js": `module.exports = 'foo'`,
+ "/Users/user/project/package.json": /* json */ `
+ {
+ "name": "@some-scope/xyz",
+ "exports": {
+ ".": "./src/index.js",
+ "./bar": {
+ "import": "./src/foo-import.js",
+ "require": "./src/foo-require.js"
+ }
+ }
+ }
+ `,
+ },
+ outfile: "/Users/user/project/out.js",
+ });
+ itBundled("packagejson/PackageJsonImportSelfUsingImportScoped", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/index.js": /* js */ `
+ import xyz from "@some-scope/xyz"
+ import foo from "@some-scope/xyz/bar"
+ export default 'index'
+ console.log(xyz, foo)
+ `,
+ "/Users/user/project/src/foo-import.js": `export default 'foo'`,
+ "/Users/user/project/src/foo-require.js": `module.exports = 'foo'`,
+ "/Users/user/project/package.json": /* json */ `
+ {
+ "name": "@some-scope/xyz",
+ "exports": {
+ ".": "./src/index.js",
+ "./bar": {
+ "import": "./src/foo-import.js",
+ "require": "./src/foo-require.js"
+ }
+ }
+ }
+ `,
+ },
+ outfile: "/Users/user/project/out.js",
+ });
+ itBundled("packagejson/PackageJsonImportSelfUsingRequireFailure", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/index.js": `require("xyz/src/foo.js")`,
+ "/Users/user/project/src/foo.js": `module.exports = 'foo'`,
+ "/Users/user/project/package.json": /* json */ `
+ {
+ "name": "xyz",
+ "exports": {
+ ".": "./src/index.js",
+ "./bar": "./src/foo.js"
+ }
+ }
+ `,
+ },
+ outfile: "/Users/user/project/out.js",
+ /* TODO FIX expectedScanLog: `Users/user/project/src/index.js: ERROR: Could not resolve "xyz/src/foo.js"
+ Users/user/project/package.json: NOTE: The path "./src/foo.js" is not exported by package "xyz":
+ Users/user/project/package.json: NOTE: The file "./src/foo.js" is exported at path "./bar":
+ Users/user/project/src/index.js: NOTE: Import from "xyz/bar" to get the file "Users/user/project/src/foo.js":
+ NOTE: You can mark the path "xyz/src/foo.js" as external to exclude it from the bundle, which will remove this error. You can also surround this "require" call with a try/catch block to handle this failure at run-time instead of bundle-time.
+ `, */
+ });
+ itBundled("packagejson/PackageJsonImportSelfUsingImportFailure", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/index.js": `import "xyz/src/foo.js"`,
+ "/Users/user/project/src/foo.js": `export default 'foo'`,
+ "/Users/user/project/package.json": /* json */ `
+ {
+ "name": "xyz",
+ "exports": {
+ ".": "./src/index.js",
+ "./bar": "./src/foo.js"
+ }
+ }
+ `,
+ },
+ outfile: "/Users/user/project/out.js",
+ /* TODO FIX expectedScanLog: `Users/user/project/src/index.js: ERROR: Could not resolve "xyz/src/foo.js"
+ Users/user/project/package.json: NOTE: The path "./src/foo.js" is not exported by package "xyz":
+ Users/user/project/package.json: NOTE: The file "./src/foo.js" is exported at path "./bar":
+ Users/user/project/src/index.js: NOTE: Import from "xyz/bar" to get the file "Users/user/project/src/foo.js":
+ NOTE: You can mark the path "xyz/src/foo.js" as external to exclude it from the bundle, which will remove this error.
+ `, */
+ });
+ itBundled("packagejson/CommonJSVariableInESMTypeModule", {
+ // GENERATED
+ files: {
+ "/entry.js": `module.exports = null`,
+ "/package.json": `{ "type": "module" }`,
+ },
+ /* TODO FIX expectedScanLog: `entry.js: WARNING: The CommonJS "module" variable is treated as a global variable in an ECMAScript module and may not work as expected
+ package.json: NOTE: This file is considered to be an ECMAScript module because the enclosing "package.json" file sets the type of this file to "module":
+ NOTE: Node's package format requires that CommonJS files in a "type": "module" package use the ".cjs" file extension.
+ `, */
+ });
+ itBundled("packagejson/PackageJsonNodePathsIssue2752", {
+ // GENERATED
+ files: {
+ "/src/entry.js": /* js */ `
+ import "pkg1"
+ import "pkg2"
+ import "@scope/pkg3/baz"
+ import "@scope/pkg4"
+ `,
+ "/usr/lib/pkg/pkg1/package.json": `{ "main": "./foo.js" }`,
+ "/usr/lib/pkg/pkg1/foo.js": `console.log('pkg1')`,
+ "/lib/pkg/pkg2/package.json": `{ "exports": { ".": "./bar.js" } }`,
+ "/lib/pkg/pkg2/bar.js": `console.log('pkg2')`,
+ "/var/lib/pkg/@scope/pkg3/package.json": `{ "browser": { "./baz.js": "./baz-browser.js" } }`,
+ "/var/lib/pkg/@scope/pkg3/baz-browser.js": `console.log('pkg3')`,
+ "/tmp/pkg/@scope/pkg4/package.json": `{ "exports": { ".": { "import": "./bat.js" } } }`,
+ "/tmp/pkg/@scope/pkg4/bat.js": `console.log('pkg4')`,
+ },
+ });
+});
diff --git a/test/bundler/esbuild/splitting.test.ts b/test/bundler/esbuild/splitting.test.ts
new file mode 100644
index 000000000..72d0d70de
--- /dev/null
+++ b/test/bundler/esbuild/splitting.test.ts
@@ -0,0 +1,390 @@
+import { itBundled, testForFile } from "../expectBundled";
+var { describe, test, expect } = testForFile(import.meta.path);
+
+// Tests ported from:
+// https://github.com/evanw/esbuild/blob/main/internal/bundler_tests/bundler_splitting_test.go
+
+// For debug, all files are written to $TEMP/bun-bundle-tests/splitting
+
+describe("bundler", () => {
+ itBundled("splitting/SplittingSharedES6IntoES6", {
+ files: {
+ "/a.js": /* js */ `
+ import {foo} from "./shared.js"
+ console.log(foo)
+ `,
+ "/b.js": /* js */ `
+ import {foo} from "./shared.js"
+ console.log(foo)
+ `,
+ "/shared.js": `export let foo = 123`,
+ },
+ entryPoints: ["/a.js", "/b.js"],
+ splitting: true,
+ format: "esm",
+ run: [
+ { file: "/out/a.js", stdout: "123" },
+ { file: "/out/b.js", stdout: "123" },
+ ],
+ assertNotPresent: {
+ "/out/a.js": "123",
+ "/out/b.js": "123",
+ },
+ });
+ return;
+ itBundled("splitting/SplittingSharedCommonJSIntoES6", {
+ // GENERATED
+ files: {
+ "/a.js": /* js */ `
+ const {foo} = require("./shared.js")
+ console.log(foo)
+ `,
+ "/b.js": /* js */ `
+ const {foo} = require("./shared.js")
+ console.log(foo)
+ `,
+ "/shared.js": `exports.foo = 123`,
+ },
+ entryPoints: ["/a.js", "/b.js"],
+ splitting: true,
+ format: "esm",
+ });
+ itBundled("splitting/SplittingDynamicES6IntoES6", {
+ // GENERATED
+ files: {
+ "/entry.js": `import("./foo.js").then(({bar}) => console.log(bar))`,
+ "/foo.js": `export let bar = 123`,
+ },
+ splitting: true,
+ format: "esm",
+ });
+ itBundled("splitting/SplittingDynamicCommonJSIntoES6", {
+ // GENERATED
+ files: {
+ "/entry.js": `import("./foo.js").then(({default: {bar}}) => console.log(bar))`,
+ "/foo.js": `exports.bar = 123`,
+ },
+ splitting: true,
+ format: "esm",
+ });
+ itBundled("splitting/SplittingDynamicAndNotDynamicES6IntoES6", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import {bar as a} from "./foo.js"
+ import("./foo.js").then(({bar: b}) => console.log(a, b))
+ `,
+ "/foo.js": `export let bar = 123`,
+ },
+ splitting: true,
+ format: "esm",
+ });
+ itBundled("splitting/SplittingDynamicAndNotDynamicCommonJSIntoES6", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import {bar as a} from "./foo.js"
+ import("./foo.js").then(({default: {bar: b}}) => console.log(a, b))
+ `,
+ "/foo.js": `exports.bar = 123`,
+ },
+ splitting: true,
+ format: "esm",
+ });
+ itBundled("splitting/SplittingAssignToLocal", {
+ // GENERATED
+ files: {
+ "/a.js": /* js */ `
+ import {foo, setFoo} from "./shared.js"
+ setFoo(123)
+ console.log(foo)
+ `,
+ "/b.js": /* js */ `
+ import {foo} from "./shared.js"
+ console.log(foo)
+ `,
+ "/shared.js": /* js */ `
+ export let foo
+ export function setFoo(value) {
+ foo = value
+ }
+ `,
+ },
+ entryPoints: ["/a.js", "/b.js"],
+ splitting: true,
+ format: "esm",
+ });
+ itBundled("splitting/SplittingSideEffectsWithoutDependencies", {
+ // GENERATED
+ files: {
+ "/a.js": /* js */ `
+ import {a} from "./shared.js"
+ console.log(a)
+ `,
+ "/b.js": /* js */ `
+ import {b} from "./shared.js"
+ console.log(b)
+ `,
+ "/shared.js": /* js */ `
+ export let a = 1
+ export let b = 2
+ console.log('side effect')
+ `,
+ },
+ entryPoints: ["/a.js", "/b.js"],
+ splitting: true,
+ format: "esm",
+ });
+ itBundled("splitting/SplittingNestedDirectories", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/pages/pageA/page.js": /* js */ `
+ import x from "../shared.js"
+ console.log(x)
+ `,
+ "/Users/user/project/src/pages/pageB/page.js": /* js */ `
+ import x from "../shared.js"
+ console.log(-x)
+ `,
+ "/Users/user/project/src/pages/shared.js": `export default 123`,
+ },
+ entryPoints: ["/Users/user/project/src/pages/pageA/page.js", "/Users/user/project/src/pages/pageB/page.js"],
+ splitting: true,
+ format: "esm",
+ });
+ itBundled("splitting/SplittingCircularReferenceIssue251", {
+ // GENERATED
+ files: {
+ "/a.js": /* js */ `
+ export * from './b.js';
+ export var p = 5;
+ `,
+ "/b.js": /* js */ `
+ export * from './a.js';
+ export var q = 6;
+ `,
+ },
+ entryPoints: ["/a.js", "/b.js"],
+ splitting: true,
+ format: "esm",
+ });
+ itBundled("splitting/SplittingMissingLazyExport", {
+ // GENERATED
+ files: {
+ "/a.js": /* js */ `
+ import {foo} from './common.js'
+ console.log(foo())
+ `,
+ "/b.js": /* js */ `
+ import {bar} from './common.js'
+ console.log(bar())
+ `,
+ "/common.js": /* js */ `
+ import * as ns from './empty.js'
+ export function foo() { return [ns, ns.missing] }
+ export function bar() { return [ns.missing] }
+ `,
+ "/empty.js": /* js */ `
+ // This forces the module into ES6 mode without importing or exporting anything
+ import.meta
+ `,
+ },
+ entryPoints: ["/a.js", "/b.js"],
+ splitting: true,
+ format: "esm",
+ /* TODO FIX expectedCompileLog: `common.js: WARNING: Import "missing" will always be undefined because the file "empty.js" has no exports
+ `, */
+ });
+ itBundled("splitting/SplittingReExportIssue273", {
+ // GENERATED
+ files: {
+ "/a.js": `export const a = 1`,
+ "/b.js": `export { a } from './a'`,
+ },
+ entryPoints: ["/a.js", "/b.js"],
+ splitting: true,
+ format: "esm",
+ });
+ itBundled("splitting/SplittingDynamicImportIssue272", {
+ // GENERATED
+ files: {
+ "/a.js": `import('./b')`,
+ "/b.js": `export default 1`,
+ },
+ entryPoints: ["/a.js", "/b.js"],
+ splitting: true,
+ format: "esm",
+ });
+ itBundled("splitting/SplittingDynamicImportOutsideSourceTreeIssue264", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry1.js": `import('package')`,
+ "/Users/user/project/src/entry2.js": `import('package')`,
+ "/Users/user/project/node_modules/package/index.js": `console.log('imported')`,
+ },
+ entryPoints: ["/Users/user/project/src/entry1.js", "/Users/user/project/src/entry2.js"],
+ splitting: true,
+ format: "esm",
+ });
+ itBundled("splitting/SplittingCrossChunkAssignmentDependencies", {
+ // GENERATED
+ files: {
+ "/a.js": /* js */ `
+ import {setValue} from './shared'
+ setValue(123)
+ `,
+ "/b.js": `import './shared'`,
+ "/shared.js": /* js */ `
+ var observer;
+ var value;
+ export function setObserver(cb) {
+ observer = cb;
+ }
+ export function getValue() {
+ return value;
+ }
+ export function setValue(next) {
+ value = next;
+ if (observer) observer();
+ }
+ sideEffects(getValue);
+ `,
+ },
+ entryPoints: ["/a.js", "/b.js"],
+ splitting: true,
+ format: "esm",
+ });
+ itBundled("splitting/SplittingCrossChunkAssignmentDependenciesRecursive", {
+ // GENERATED
+ files: {
+ "/a.js": /* js */ `
+ import { setX } from './x'
+ setX()
+ `,
+ "/b.js": /* js */ `
+ import { setZ } from './z'
+ setZ()
+ `,
+ "/c.js": /* js */ `
+ import { setX2 } from './x'
+ import { setY2 } from './y'
+ import { setZ2 } from './z'
+ setX2();
+ setY2();
+ setZ2();
+ `,
+ "/x.js": /* js */ `
+ let _x
+ export function setX(v) { _x = v }
+ export function setX2(v) { _x = v }
+ `,
+ "/y.js": /* js */ `
+ import { setX } from './x'
+ let _y
+ export function setY(v) { _y = v }
+ export function setY2(v) { setX(v); _y = v }
+ `,
+ "/z.js": /* js */ `
+ import { setY } from './y'
+ let _z
+ export function setZ(v) { _z = v }
+ export function setZ2(v) { setY(v); _z = v }
+ `,
+ },
+ entryPoints: ["/a.js", "/b.js", "/c.js"],
+ splitting: true,
+ format: "esm",
+ });
+ itBundled("splitting/SplittingDuplicateChunkCollision", {
+ // GENERATED
+ files: {
+ "/a.js": `import "./ab"`,
+ "/b.js": `import "./ab"`,
+ "/c.js": `import "./cd"`,
+ "/d.js": `import "./cd"`,
+ "/ab.js": `console.log(123)`,
+ "/cd.js": `console.log(123)`,
+ },
+ entryPoints: ["/a.js", "/b.js", "/c.js", "/d.js"],
+ splitting: true,
+ minifyWhitespace: true,
+ format: "esm",
+ });
+ itBundled("splitting/SplittingMinifyIdentifiersCrashIssue437", {
+ // GENERATED
+ files: {
+ "/a.js": /* js */ `
+ import {foo} from "./shared"
+ console.log(foo)
+ `,
+ "/b.js": /* js */ `
+ import {foo} from "./shared"
+ console.log(foo)
+ `,
+ "/c.js": `import "./shared"`,
+ "/shared.js": `export function foo(bar) {}`,
+ },
+ entryPoints: ["/a.js", "/b.js", "/c.js"],
+ splitting: true,
+ minifyIdentifiers: true,
+ format: "esm",
+ });
+ itBundled("splitting/SplittingHybridESMAndCJSIssue617", {
+ // GENERATED
+ files: {
+ "/a.js": `export let foo`,
+ "/b.js": `export let bar = require('./a')`,
+ },
+ entryPoints: ["/a.js", "/b.js"],
+ splitting: true,
+ format: "esm",
+ });
+ itBundled("splitting/SplittingPublicPathEntryName", {
+ // GENERATED
+ files: {
+ "/a.js": `import("./b")`,
+ "/b.js": `console.log('b')`,
+ },
+ splitting: true,
+ format: "esm",
+ publicPath: "/www",
+ });
+ itBundled("splitting/SplittingChunkPathDirPlaceholderImplicitOutbase", {
+ // GENERATED
+ files: {
+ "/project/entry.js": `console.log(import('./output-path/should-contain/this-text/file'))`,
+ "/project/output-path/should-contain/this-text/file.js": `console.log('file.js')`,
+ },
+ format: "esm",
+ splitting: true,
+ });
+ itBundled("splitting/EdgeCaseIssue2793WithSplitting", {
+ // GENERATED
+ files: {
+ "/src/a.js": `export const A = 42;`,
+ "/src/b.js": `export const B = async () => (await import(".")).A`,
+ "/src/index.js": /* js */ `
+ export * from "./a"
+ export * from "./b"
+ `,
+ },
+ outdir: "/out",
+ entryPoints: ["/src/index.js"],
+ format: "esm",
+ splitting: true,
+ });
+ itBundled("splitting/EdgeCaseIssue2793WithoutSplitting", {
+ // GENERATED
+ files: {
+ "/src/a.js": `export const A = 42;`,
+ "/src/b.js": `export const B = async () => (await import(".")).A`,
+ "/src/index.js": /* js */ `
+ export * from "./a"
+ export * from "./b"
+ `,
+ },
+ entryPoints: ["/src/index.js"],
+ format: "esm",
+ outdir: "/out",
+ });
+});
diff --git a/test/bundler/esbuild/ts.test.ts b/test/bundler/esbuild/ts.test.ts
new file mode 100644
index 000000000..7ad0453c0
--- /dev/null
+++ b/test/bundler/esbuild/ts.test.ts
@@ -0,0 +1,1638 @@
+import { itBundled, testForFile } from "../expectBundled";
+var { describe, test, expect } = testForFile(import.meta.path);
+
+// Tests ported from:
+// https://github.com/evanw/esbuild/blob/main/internal/bundler_tests/bundler_ts_test.go
+
+// For debug, all files are written to $TEMP/bun-bundle-tests/ts
+
+describe("bundler", () => {
+ return;
+ itBundled("ts/TSDeclareConst", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ declare const require: any
+ declare const exports: any;
+ declare const module: any
+
+ declare const foo: any
+ let foo = bar()
+ `,
+ },
+ });
+ itBundled("ts/TSDeclareLet", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ declare let require: any
+ declare let exports: any;
+ declare let module: any
+
+ declare let foo: any
+ let foo = bar()
+ `,
+ },
+ });
+ itBundled("ts/TSDeclareVar", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ declare var require: any
+ declare var exports: any;
+ declare var module: any
+
+ declare var foo: any
+ let foo = bar()
+ `,
+ },
+ });
+ itBundled("ts/TSDeclareClass", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ declare class require {}
+ declare class exports {};
+ declare class module {}
+
+ declare class foo {}
+ let foo = bar()
+ `,
+ },
+ });
+ itBundled("ts/TSDeclareClassFields", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import './define-false'
+ import './define-true'
+ `,
+ "/define-false/index.ts": /* ts */ `
+ class Foo {
+ a
+ declare b
+ [(() => null, c)]
+ declare [(() => null, d)]
+
+ static A
+ static declare B
+ static [(() => null, C)]
+ static declare [(() => null, D)]
+ }
+ (() => new Foo())()
+ `,
+ "/define-true/index.ts": /* ts */ `
+ class Bar {
+ a
+ declare b
+ [(() => null, c)]
+ declare [(() => null, d)]
+
+ static A
+ static declare B
+ static [(() => null, C)]
+ static declare [(() => null, D)]
+ }
+ (() => new Bar())()
+ `,
+ "/define-true/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "useDefineForClassFields": true
+ }
+ }
+ `,
+ },
+ });
+ itBundled("ts/TSDeclareFunction", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ declare function require(): void
+ declare function exports(): void;
+ declare function module(): void
+
+ declare function foo() {}
+ let foo = bar()
+ `,
+ },
+ });
+ itBundled("ts/TSDeclareNamespace", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ declare namespace require {}
+ declare namespace exports {};
+ declare namespace module {}
+
+ declare namespace foo {}
+ let foo = bar()
+ `,
+ },
+ });
+ itBundled("ts/TSDeclareEnum", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ declare enum require {}
+ declare enum exports {};
+ declare enum module {}
+
+ declare enum foo {}
+ let foo = bar()
+ `,
+ },
+ });
+ itBundled("ts/TSDeclareConstEnum", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ declare const enum require {}
+ declare const enum exports {};
+ declare const enum module {}
+
+ declare const enum foo {}
+ let foo = bar()
+ `,
+ },
+ });
+ itBundled("ts/TSConstEnumComments", {
+ // GENERATED
+ files: {
+ "/bar.ts": /* ts */ `
+ export const enum Foo {
+ "%/*" = 1,
+ "*/%" = 2,
+ }
+ `,
+ "/foo.ts": /* ts */ `
+ import { Foo } from "./bar";
+ const enum Bar {
+ "%/*" = 1,
+ "*/%" = 2,
+ }
+ console.log({
+ 'should have comments': [
+ Foo["%/*"],
+ Bar["%/*"],
+ ],
+ 'should not have comments': [
+ Foo["*/%"],
+ Bar["*/%"],
+ ],
+ });
+ `,
+ },
+ entryPoints: ["/foo.ts"],
+ });
+ itBundled("ts/TSImportEmptyNamespace", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import {ns} from './ns.ts'
+ function foo(): ns.type {}
+ foo();
+ `,
+ "/ns.ts": `export namespace ns {}`,
+ },
+ });
+ itBundled("ts/TSImportMissingES6", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import fn, {x as a, y as b} from './foo'
+ console.log(fn(a, b))
+ `,
+ "/foo.js": `export const x = 123`,
+ },
+ /* TODO FIX expectedCompileLog: `entry.ts: ERROR: No matching export in "foo.js" for import "default"
+ entry.ts: ERROR: No matching export in "foo.js" for import "y"
+ `, */
+ });
+ itBundled("ts/TSImportMissingUnusedES6", {
+ // GENERATED
+ files: {
+ "/entry.ts": `import fn, {x as a, y as b} from './foo'`,
+ "/foo.js": `export const x = 123`,
+ },
+ });
+ itBundled("ts/TSExportMissingES6", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import * as ns from './foo'
+ console.log(ns)
+ `,
+ "/foo.ts": `export {nope} from './bar'`,
+ "/bar.js": `export const yep = 123`,
+ },
+ });
+ itBundled("ts/TSImportMissingFile", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import {Something} from './doesNotExist.ts'
+ let foo = new Something
+ `,
+ },
+ /* TODO FIX expectedScanLog: `entry.ts: ERROR: Could not resolve "./doesNotExist.ts"
+ `, */
+ });
+ itBundled("ts/TSImportTypeOnlyFile", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import {SomeType1} from './doesNotExist1.ts'
+ import {SomeType2} from './doesNotExist2.ts'
+ let foo: SomeType1 = bar()
+ `,
+ },
+ });
+ itBundled("ts/TSExportEquals", {
+ // GENERATED
+ files: {
+ "/a.ts": /* ts */ `
+ import b from './b.ts'
+ console.log(b)
+ `,
+ "/b.ts": /* ts */ `
+ export = [123, foo]
+ function foo() {}
+ `,
+ },
+ });
+ itBundled("ts/TSExportNamespace", {
+ // GENERATED
+ files: {
+ "/a.ts": /* ts */ `
+ import {Foo} from './b.ts'
+ console.log(new Foo)
+ `,
+ "/b.ts": /* ts */ `
+ export class Foo {}
+ export namespace Foo {
+ export let foo = 1
+ }
+ export namespace Foo {
+ export let bar = 2
+ }
+ `,
+ },
+ });
+ itBundled("ts/TSMinifyEnum", {
+ // GENERATED
+ files: {
+ "/a.ts": `enum Foo { A, B, C = Foo }`,
+ "/b.ts": `export enum Foo { X, Y, Z = Foo }`,
+ },
+ entryPoints: ["/a.ts", "/b.ts"],
+ minifySyntax: true,
+ minifyWhitespace: true,
+ minifyIdentifiers: true,
+ mode: "transform",
+ });
+ itBundled("ts/TSMinifyNestedEnum", {
+ // GENERATED
+ files: {
+ "/a.ts": `function foo() { enum Foo { A, B, C = Foo } return Foo }`,
+ "/b.ts": `export function foo() { enum Foo { X, Y, Z = Foo } return Foo }`,
+ },
+ entryPoints: ["/a.ts", "/b.ts"],
+ minifySyntax: true,
+ minifyWhitespace: true,
+ minifyIdentifiers: true,
+ mode: "transform",
+ });
+ itBundled("ts/TSMinifyNestedEnumNoLogicalAssignment", {
+ // GENERATED
+ files: {
+ "/a.ts": `function foo() { enum Foo { A, B, C = Foo } return Foo }`,
+ "/b.ts": `export function foo() { enum Foo { X, Y, Z = Foo } return Foo }`,
+ },
+ entryPoints: ["/a.ts", "/b.ts"],
+ minifySyntax: true,
+ minifyWhitespace: true,
+ minifyIdentifiers: true,
+ outdir: "/",
+ mode: "transform",
+ });
+ itBundled("ts/TSMinifyNestedEnumNoArrow", {
+ // GENERATED
+ files: {
+ "/a.ts": `function foo() { enum Foo { A, B, C = Foo } return Foo }`,
+ "/b.ts": `export function foo() { enum Foo { X, Y, Z = Foo } return Foo }`,
+ },
+ entryPoints: ["/a.ts", "/b.ts"],
+ minifySyntax: true,
+ minifyWhitespace: true,
+ minifyIdentifiers: true,
+ outdir: "/",
+ mode: "transform",
+ });
+ itBundled("ts/TSMinifyNamespace", {
+ // GENERATED
+ files: {
+ "/a.ts": /* ts */ `
+ namespace Foo {
+ export namespace Bar {
+ foo(Foo, Bar)
+ }
+ }
+ `,
+ "/b.ts": /* ts */ `
+ export namespace Foo {
+ export namespace Bar {
+ foo(Foo, Bar)
+ }
+ }
+ `,
+ },
+ entryPoints: ["/a.ts", "/b.ts"],
+ minifySyntax: true,
+ minifyWhitespace: true,
+ minifyIdentifiers: true,
+ mode: "transform",
+ });
+ itBundled("ts/TSMinifyNamespaceNoLogicalAssignment", {
+ // GENERATED
+ files: {
+ "/a.ts": /* ts */ `
+ namespace Foo {
+ export namespace Bar {
+ foo(Foo, Bar)
+ }
+ }
+ `,
+ "/b.ts": /* ts */ `
+ export namespace Foo {
+ export namespace Bar {
+ foo(Foo, Bar)
+ }
+ }
+ `,
+ },
+ entryPoints: ["/a.ts", "/b.ts"],
+ minifySyntax: true,
+ minifyWhitespace: true,
+ minifyIdentifiers: true,
+ outdir: "/",
+ mode: "transform",
+ });
+ itBundled("ts/TSMinifyNamespaceNoArrow", {
+ // GENERATED
+ files: {
+ "/a.ts": /* ts */ `
+ namespace Foo {
+ export namespace Bar {
+ foo(Foo, Bar)
+ }
+ }
+ `,
+ "/b.ts": /* ts */ `
+ export namespace Foo {
+ export namespace Bar {
+ foo(Foo, Bar)
+ }
+ }
+ `,
+ },
+ entryPoints: ["/a.ts", "/b.ts"],
+ minifySyntax: true,
+ minifyWhitespace: true,
+ minifyIdentifiers: true,
+ outdir: "/",
+ mode: "transform",
+ });
+ itBundled("ts/TSMinifyDerivedClass", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ class Foo extends Bar {
+ foo = 1;
+ bar = 2;
+ constructor() {
+ super();
+ foo();
+ bar();
+ }
+ }
+ `,
+ },
+ minifySyntax: true,
+ unsupportedJSFeatures: "es2015",
+ mode: "transform",
+ });
+ itBundled("ts/TSImportVsLocalCollisionAllTypes", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import {a, b, c, d, e} from './other.ts'
+ let a
+ const b = 0
+ var c
+ function d() {}
+ class e {}
+ console.log(a, b, c, d, e)
+ `,
+ "/other.ts": ``,
+ },
+ });
+ itBundled("ts/TSImportVsLocalCollisionMixed", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import {a, b, c, d, e, real} from './other.ts'
+ let a
+ const b = 0
+ var c
+ function d() {}
+ class e {}
+ console.log(a, b, c, d, e, real)
+ `,
+ "/other.ts": `export let real = 123`,
+ },
+ });
+ itBundled("ts/TSImportEqualsEliminationTest", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import a = foo.a
+ import b = a.b
+ import c = b.c
+
+ import x = foo.x
+ import y = x.y
+ import z = y.z
+
+ export let bar = c
+ `,
+ },
+ });
+ itBundled("ts/TSImportEqualsTreeShakingFalse", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import { foo } from 'pkg'
+ import used = foo.used
+ import unused = foo.unused
+ export { used }
+ `,
+ },
+ mode: "passthrough",
+ });
+ itBundled("ts/TSImportEqualsTreeShakingTrue", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import { foo } from 'pkg'
+ import used = foo.used
+ import unused = foo.unused
+ export { used }
+ `,
+ },
+ mode: "passthrough",
+ });
+ itBundled("ts/TSImportEqualsBundle", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import { foo } from 'pkg'
+ import used = foo.used
+ import unused = foo.unused
+ export { used }
+ `,
+ },
+ });
+ itBundled("ts/TSMinifiedBundleES6", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import {foo} from './a'
+ console.log(foo())
+ `,
+ "/a.ts": /* ts */ `
+ export function foo() {
+ return 123
+ }
+ `,
+ },
+ minifySyntax: true,
+ minifyWhitespace: true,
+ minifyIdentifiers: true,
+ });
+ itBundled("ts/TSMinifiedBundleCommonJS", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ const {foo} = require('./a')
+ console.log(foo(), require('./j.json'))
+ `,
+ "/a.ts": /* ts */ `
+ exports.foo = function() {
+ return 123
+ }
+ `,
+ "/j.json": `{"test": true}`,
+ },
+ minifySyntax: true,
+ minifyWhitespace: true,
+ minifyIdentifiers: true,
+ });
+ itBundled("ts/TypeScriptDecorators", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import all from './all'
+ import all_computed from './all_computed'
+ import {a} from './a'
+ import {b} from './b'
+ import {c} from './c'
+ import {d} from './d'
+ import e from './e'
+ import f from './f'
+ import g from './g'
+ import h from './h'
+ import {i} from './i'
+ import {j} from './j'
+ import k from './k'
+ import {fn} from './arguments'
+ console.log(all, all_computed, a, b, c, d, e, f, g, h, i, j, k, fn)
+ `,
+ "/all.ts": /* ts */ `
+ @x.y()
+ @new y.x()
+ export default class Foo {
+ @x @y mUndef
+ @x @y mDef = 1
+ @x @y method(@x0 @y0 arg0, @x1 @y1 arg1) { return new Foo }
+ @x @y declare mDecl
+ constructor(@x0 @y0 arg0, @x1 @y1 arg1) {}
+
+ @x @y static sUndef
+ @x @y static sDef = new Foo
+ @x @y static sMethod(@x0 @y0 arg0, @x1 @y1 arg1) { return new Foo }
+ @x @y static declare mDecl
+ }
+ `,
+ "/all_computed.ts": /* ts */ `
+ @x?.[_ + 'y']()
+ @new y?.[_ + 'x']()
+ export default class Foo {
+ @x @y [mUndef()]
+ @x @y [mDef()] = 1
+ @x @y [method()](@x0 @y0 arg0, @x1 @y1 arg1) { return new Foo }
+ @x @y declare [mDecl()]
+
+ // Side effect order must be preserved even for fields without decorators
+ [xUndef()]
+ [xDef()] = 2
+ static [yUndef()]
+ static [yDef()] = 3
+
+ @x @y static [sUndef()]
+ @x @y static [sDef()] = new Foo
+ @x @y static [sMethod()](@x0 @y0 arg0, @x1 @y1 arg1) { return new Foo }
+ @x @y static declare [mDecl()]
+ }
+ `,
+ "/a.ts": /* ts */ `
+ @x(() => 0) @y(() => 1)
+ class a_class {
+ fn() { return new a_class }
+ static z = new a_class
+ }
+ export let a = a_class
+ `,
+ "/b.ts": /* ts */ `
+ @x(() => 0) @y(() => 1)
+ abstract class b_class {
+ fn() { return new b_class }
+ static z = new b_class
+ }
+ export let b = b_class
+ `,
+ "/c.ts": /* ts */ `
+ @x(() => 0) @y(() => 1)
+ export class c {
+ fn() { return new c }
+ static z = new c
+ }
+ `,
+ "/d.ts": /* ts */ `
+ @x(() => 0) @y(() => 1)
+ export abstract class d {
+ fn() { return new d }
+ static z = new d
+ }
+ `,
+ "/e.ts": /* ts */ `
+ @x(() => 0) @y(() => 1)
+ export default class {}
+ `,
+ "/f.ts": /* ts */ `
+ @x(() => 0) @y(() => 1)
+ export default class f {
+ fn() { return new f }
+ static z = new f
+ }
+ `,
+ "/g.ts": /* ts */ `
+ @x(() => 0) @y(() => 1)
+ export default abstract class {}
+ `,
+ "/h.ts": /* ts */ `
+ @x(() => 0) @y(() => 1)
+ export default abstract class h {
+ fn() { return new h }
+ static z = new h
+ }
+ `,
+ "/i.ts": /* ts */ `
+ class i_class {
+ @x(() => 0) @y(() => 1)
+ foo
+ }
+ export let i = i_class
+ `,
+ "/j.ts": /* ts */ `
+ export class j {
+ @x(() => 0) @y(() => 1)
+ foo() {}
+ }
+ `,
+ "/k.ts": /* ts */ `
+ export default class {
+ foo(@x(() => 0) @y(() => 1) x) {}
+ }
+ `,
+ "/arguments.ts": /* ts */ `
+ function dec(x: any): any {}
+ export function fn(x: string): any {
+ class Foo {
+ @dec(arguments[0])
+ [arguments[0]]() {}
+ }
+ return Foo;
+ }
+ `,
+ },
+ });
+ itBundled("ts/TypeScriptDecoratorsKeepNames", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ @decoratorMustComeAfterName
+ class Foo {}
+ `,
+ },
+ });
+ itBundled("ts/TypeScriptDecoratorScopeIssue2147", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ let foo = 1
+ class Foo {
+ method1(@dec(foo) foo = 2) {}
+ method2(@dec(() => foo) foo = 3) {}
+ }
+
+ class Bar {
+ static x = class {
+ static y = () => {
+ let bar = 1
+ @dec(bar)
+ @dec(() => bar)
+ class Baz {
+ @dec(bar) method1() {}
+ @dec(() => bar) method2() {}
+ method3(@dec(() => bar) bar) {}
+ method4(@dec(() => bar) bar) {}
+ }
+ return Baz
+ }
+ }
+ }
+ `,
+ },
+ mode: "passthrough",
+ });
+ itBundled("ts/TSExportDefaultTypeIssue316", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import dc_def, { bar as dc } from './keep/declare-class'
+ import dl_def, { bar as dl } from './keep/declare-let'
+ import im_def, { bar as im } from './keep/interface-merged'
+ import in_def, { bar as _in } from './keep/interface-nested'
+ import tn_def, { bar as tn } from './keep/type-nested'
+ import vn_def, { bar as vn } from './keep/value-namespace'
+ import vnm_def, { bar as vnm } from './keep/value-namespace-merged'
+
+ import i_def, { bar as i } from './remove/interface'
+ import ie_def, { bar as ie } from './remove/interface-exported'
+ import t_def, { bar as t } from './remove/type'
+ import te_def, { bar as te } from './remove/type-exported'
+ import ton_def, { bar as ton } from './remove/type-only-namespace'
+ import tone_def, { bar as tone } from './remove/type-only-namespace-exported'
+
+ export default [
+ dc_def, dc,
+ dl_def, dl,
+ im_def, im,
+ in_def, _in,
+ tn_def, tn,
+ vn_def, vn,
+ vnm_def, vnm,
+
+ i,
+ ie,
+ t,
+ te,
+ ton,
+ tone,
+ ]
+ `,
+ "/keep/declare-class.ts": /* ts */ `
+ declare class foo {}
+ export default foo
+ export let bar = 123
+ `,
+ "/keep/declare-let.ts": /* ts */ `
+ declare let foo: number
+ export default foo
+ export let bar = 123
+ `,
+ "/keep/interface-merged.ts": /* ts */ `
+ class foo {
+ static x = new foo
+ }
+ interface foo {}
+ export default foo
+ export let bar = 123
+ `,
+ "/keep/interface-nested.ts": /* ts */ `
+ if (true) {
+ interface foo {}
+ }
+ export default foo
+ export let bar = 123
+ `,
+ "/keep/type-nested.ts": /* ts */ `
+ if (true) {
+ type foo = number
+ }
+ export default foo
+ export let bar = 123
+ `,
+ "/keep/value-namespace.ts": /* ts */ `
+ namespace foo {
+ export let num = 0
+ }
+ export default foo
+ export let bar = 123
+ `,
+ "/keep/value-namespace-merged.ts": /* ts */ `
+ namespace foo {
+ export type num = number
+ }
+ namespace foo {
+ export let num = 0
+ }
+ export default foo
+ export let bar = 123
+ `,
+ "/remove/interface.ts": /* ts */ `
+ interface foo { }
+ export default foo
+ export let bar = 123
+ `,
+ "/remove/interface-exported.ts": /* ts */ `
+ export interface foo { }
+ export default foo
+ export let bar = 123
+ `,
+ "/remove/type.ts": /* ts */ `
+ type foo = number
+ export default foo
+ export let bar = 123
+ `,
+ "/remove/type-exported.ts": /* ts */ `
+ export type foo = number
+ export default foo
+ export let bar = 123
+ `,
+ "/remove/type-only-namespace.ts": /* ts */ `
+ namespace foo {
+ export type num = number
+ }
+ export default foo
+ export let bar = 123
+ `,
+ "/remove/type-only-namespace-exported.ts": /* ts */ `
+ export namespace foo {
+ export type num = number
+ }
+ export default foo
+ export let bar = 123
+ `,
+ },
+ });
+ itBundled("ts/TSImplicitExtensions", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import './pick-js.js'
+ import './pick-ts.js'
+ import './pick-jsx.jsx'
+ import './pick-tsx.jsx'
+ import './order-js.js'
+ import './order-jsx.jsx'
+ `,
+ "/pick-js.js": `console.log("correct")`,
+ "/pick-js.ts": `console.log("wrong")`,
+ "/pick-ts.jsx": `console.log("wrong")`,
+ "/pick-ts.ts": `console.log("correct")`,
+ "/pick-jsx.jsx": `console.log("correct")`,
+ "/pick-jsx.tsx": `console.log("wrong")`,
+ "/pick-tsx.js": `console.log("wrong")`,
+ "/pick-tsx.tsx": `console.log("correct")`,
+ "/order-js.ts": `console.log("correct")`,
+ "/order-js.tsx": `console.log("wrong")`,
+ "/order-jsx.ts": `console.log("correct")`,
+ "/order-jsx.tsx": `console.log("wrong")`,
+ },
+ });
+ itBundled("ts/TSImplicitExtensionsMissing", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import './mjs.mjs'
+ import './cjs.cjs'
+ import './js.js'
+ import './jsx.jsx'
+ `,
+ "/mjs.ts": ``,
+ "/mjs.tsx": ``,
+ "/cjs.ts": ``,
+ "/cjs.tsx": ``,
+ "/js.ts.js": ``,
+ "/jsx.tsx.jsx": ``,
+ },
+ /* TODO FIX expectedScanLog: `entry.ts: ERROR: Could not resolve "./mjs.mjs"
+ entry.ts: ERROR: Could not resolve "./cjs.cjs"
+ entry.ts: ERROR: Could not resolve "./js.js"
+ entry.ts: ERROR: Could not resolve "./jsx.jsx"
+ `, */
+ });
+ itBundled("ts/ExportTypeIssue379", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import * as A from './a'
+ import * as B from './b'
+ import * as C from './c'
+ import * as D from './d'
+ console.log(A, B, C, D)
+ `,
+ "/a.ts": /* ts */ `
+ type Test = Element
+ let foo = 123
+ export { Test, foo }
+ `,
+ "/b.ts": /* ts */ `
+ export type Test = Element
+ export let foo = 123
+ `,
+ "/c.ts": /* ts */ `
+ import { Test } from './test'
+ let foo = 123
+ export { Test }
+ export { foo }
+ `,
+ "/d.ts": /* ts */ `
+ export { Test }
+ export { foo }
+ import { Test } from './test'
+ let foo = 123
+ `,
+ "/test.ts": `export type Test = Element`,
+ },
+ });
+ itBundled("ts/ThisInsideFunctionTS", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ function foo(x = this) { console.log(this) }
+ const objFoo = {
+ foo(x = this) { console.log(this) }
+ }
+ class Foo {
+ x = this
+ static y = this.z
+ foo(x = this) { console.log(this) }
+ static bar(x = this) { console.log(this) }
+ }
+ new Foo(foo(objFoo))
+ if (nested) {
+ function bar(x = this) { console.log(this) }
+ const objBar = {
+ foo(x = this) { console.log(this) }
+ }
+ class Bar {
+ x = this
+ static y = this.z
+ foo(x = this) { console.log(this) }
+ static bar(x = this) { console.log(this) }
+ }
+ new Bar(bar(objBar))
+ }
+ `,
+ },
+ });
+ itBundled("ts/ThisInsideFunctionTSUseDefineForClassFields", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ function foo(x = this) { console.log(this) }
+ const objFoo = {
+ foo(x = this) { console.log(this) }
+ }
+ class Foo {
+ x = this
+ static y = this.z
+ foo(x = this) { console.log(this) }
+ static bar(x = this) { console.log(this) }
+ }
+ new Foo(foo(objFoo))
+ if (nested) {
+ function bar(x = this) { console.log(this) }
+ const objBar = {
+ foo(x = this) { console.log(this) }
+ }
+ class Bar {
+ x = this
+ static y = this.z
+ foo(x = this) { console.log(this) }
+ static bar(x = this) { console.log(this) }
+ }
+ new Bar(bar(objBar))
+ }
+ `,
+ },
+ });
+ itBundled("ts/ThisInsideFunctionTSNoBundle", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ function foo(x = this) { console.log(this) }
+ const objFoo = {
+ foo(x = this) { console.log(this) }
+ }
+ class Foo {
+ x = this
+ static y = this.z
+ foo(x = this) { console.log(this) }
+ static bar(x = this) { console.log(this) }
+ }
+ new Foo(foo(objFoo))
+ if (nested) {
+ function bar(x = this) { console.log(this) }
+ const objBar = {
+ foo(x = this) { console.log(this) }
+ }
+ class Bar {
+ x = this
+ static y = this.z
+ foo(x = this) { console.log(this) }
+ static bar(x = this) { console.log(this) }
+ }
+ new Bar(bar(objBar))
+ }
+ `,
+ },
+ mode: "passthrough",
+ });
+ itBundled("ts/ThisInsideFunctionTSNoBundleUseDefineForClassFields", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ function foo(x = this) { console.log(this) }
+ const objFoo = {
+ foo(x = this) { console.log(this) }
+ }
+ class Foo {
+ x = this
+ static y = this.z
+ foo(x = this) { console.log(this) }
+ static bar(x = this) { console.log(this) }
+ }
+ new Foo(foo(objFoo))
+ if (nested) {
+ function bar(x = this) { console.log(this) }
+ const objBar = {
+ foo(x = this) { console.log(this) }
+ }
+ class Bar {
+ x = this
+ static y = this.z
+ foo(x = this) { console.log(this) }
+ static bar(x = this) { console.log(this) }
+ }
+ new Bar(bar(objBar))
+ }
+ `,
+ },
+ mode: "passthrough",
+ });
+ itBundled("ts/TSComputedClassFieldUseDefineFalse", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ class Foo {
+ [q];
+ [r] = s;
+ @dec
+ [x];
+ @dec
+ [y] = z;
+ }
+ new Foo()
+ `,
+ },
+ mode: "passthrough",
+ });
+ itBundled("ts/TSComputedClassFieldUseDefineTrue", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ class Foo {
+ [q];
+ [r] = s;
+ @dec
+ [x];
+ @dec
+ [y] = z;
+ }
+ new Foo()
+ `,
+ },
+ mode: "passthrough",
+ });
+ itBundled("ts/TSComputedClassFieldUseDefineTrueLower", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ class Foo {
+ [q];
+ [r] = s;
+ @dec
+ [x];
+ @dec
+ [y] = z;
+ }
+ new Foo()
+ `,
+ },
+ useDefineForClassFields: true,
+ mode: "passthrough",
+ });
+ itBundled("ts/TSAbstractClassFieldUseAssign", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ const keepThis = Symbol('keepThis')
+ declare const AND_REMOVE_THIS: unique symbol
+ abstract class Foo {
+ REMOVE_THIS: any
+ [keepThis]: any
+ abstract REMOVE_THIS_TOO: any
+ abstract [AND_REMOVE_THIS]: any
+ abstract [(x => y => x + y)('nested')('scopes')]: any
+ }
+ (() => new Foo())()
+ `,
+ },
+ mode: "passthrough",
+ });
+ itBundled("ts/TSAbstractClassFieldUseDefine", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ const keepThisToo = Symbol('keepThisToo')
+ declare const REMOVE_THIS_TOO: unique symbol
+ abstract class Foo {
+ keepThis: any
+ [keepThisToo]: any
+ abstract REMOVE_THIS: any
+ abstract [REMOVE_THIS_TOO]: any
+ abstract [(x => y => x + y)('nested')('scopes')]: any
+ }
+ (() => new Foo())()
+ `,
+ },
+ mode: "passthrough",
+ });
+ itBundled("ts/TSImportMTS", {
+ // GENERATED
+ files: {
+ "/entry.ts": `import './imported.mjs'`,
+ "/imported.mts": `console.log('works')`,
+ },
+ });
+ itBundled("ts/TSImportCTS", {
+ // GENERATED
+ files: {
+ "/entry.ts": `require('./required.cjs')`,
+ "/required.cjs": `console.log('works')`,
+ },
+ });
+ itBundled("ts/TSSideEffectsFalseWarningTypeDeclarations", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import "some-js"
+ import "some-ts"
+ import "empty-js"
+ import "empty-ts"
+ import "empty-dts"
+ `,
+ "/node_modules/some-js/package.json": `{ "main": "./foo.js", "sideEffects": false }`,
+ "/node_modules/some-js/foo.js": `console.log('foo')`,
+ "/node_modules/some-ts/package.json": `{ "main": "./foo.ts", "sideEffects": false }`,
+ "/node_modules/some-ts/foo.ts": `console.log('foo' as string)`,
+ "/node_modules/empty-js/package.json": `{ "main": "./foo.js", "sideEffects": false }`,
+ "/node_modules/empty-js/foo.js": ``,
+ "/node_modules/empty-ts/package.json": `{ "main": "./foo.ts", "sideEffects": false }`,
+ "/node_modules/empty-ts/foo.ts": `export type Foo = number`,
+ "/node_modules/empty-dts/package.json": `{ "main": "./foo.d.ts", "sideEffects": false }`,
+ "/node_modules/empty-dts/foo.d.ts": `export type Foo = number`,
+ },
+ /* TODO FIX expectedScanLog: `entry.ts: WARNING: Ignoring this import because "node_modules/some-js/foo.js" was marked as having no side effects
+ node_modules/some-js/package.json: NOTE: "sideEffects" is false in the enclosing "package.json" file:
+ entry.ts: WARNING: Ignoring this import because "node_modules/some-ts/foo.ts" was marked as having no side effects
+ node_modules/some-ts/package.json: NOTE: "sideEffects" is false in the enclosing "package.json" file:
+ `, */
+ });
+ itBundled("ts/TSSiblingNamespace", {
+ // GENERATED
+ files: {
+ "/let.ts": /* ts */ `
+ export namespace x { export let y = 123 }
+ export namespace x { export let z = y }
+ `,
+ "/function.ts": /* ts */ `
+ export namespace x { export function y() {} }
+ export namespace x { export let z = y }
+ `,
+ "/class.ts": /* ts */ `
+ export namespace x { export class y {} }
+ export namespace x { export let z = y }
+ `,
+ "/namespace.ts": /* ts */ `
+ export namespace x { export namespace y { 0 } }
+ export namespace x { export let z = y }
+ `,
+ "/enum.ts": /* ts */ `
+ export namespace x { export enum y {} }
+ export namespace x { export let z = y }
+ `,
+ },
+ entryPoints: ["/let.ts", "/function.ts", "/class.ts", "/namespace.ts", "/enum.ts"],
+ mode: "passthrough",
+ });
+ itBundled("ts/TSSiblingEnum", {
+ // GENERATED
+ files: {
+ "/number.ts": /* ts */ `
+ export enum x { y, yy = y }
+ export enum x { z = y + 1 }
+
+ declare let y: any, z: any
+ export namespace x { console.log(y, z) }
+ console.log(x.y, x.z)
+ `,
+ "/string.ts": /* ts */ `
+ export enum x { y = 'a', yy = y }
+ export enum x { z = y }
+
+ declare let y: any, z: any
+ export namespace x { console.log(y, z) }
+ console.log(x.y, x.z)
+ `,
+ "/propagation.ts": /* ts */ `
+ export enum a { b = 100 }
+ export enum x {
+ c = a.b,
+ d = c * 2,
+ e = x.d ** 2,
+ f = x['e'] / 4,
+ }
+ export enum x { g = f >> 4 }
+ console.log(a.b, a['b'], x.g, x['g'])
+ `,
+ "/nested-number.ts": /* ts */ `
+ export namespace foo { export enum x { y, yy = y } }
+ export namespace foo { export enum x { z = y + 1 } }
+
+ declare let y: any, z: any
+ export namespace foo.x {
+ console.log(y, z)
+ console.log(x.y, x.z)
+ }
+ `,
+ "/nested-string.ts": /* ts */ `
+ export namespace foo { export enum x { y = 'a', yy = y } }
+ export namespace foo { export enum x { z = y } }
+
+ declare let y: any, z: any
+ export namespace foo.x {
+ console.log(y, z)
+ console.log(x.y, x.z)
+ }
+ `,
+ "/nested-propagation.ts": /* ts */ `
+ export namespace n { export enum a { b = 100 } }
+ export namespace n {
+ export enum x {
+ c = n.a.b,
+ d = c * 2,
+ e = x.d ** 2,
+ f = x['e'] / 4,
+ }
+ }
+ export namespace n {
+ export enum x { g = f >> 4 }
+ console.log(a.b, n.a.b, n['a']['b'], x.g, n.x.g, n['x']['g'])
+ }
+ `,
+ },
+ entryPoints: [
+ "/number.ts",
+ "/string.ts",
+ "/propagation.ts",
+ "/nested-number.ts",
+ "/nested-string.ts",
+ "/nested-propagation.ts",
+ ],
+ mode: "passthrough",
+ });
+ itBundled("ts/TSEnumTreeShaking", {
+ // GENERATED
+ files: {
+ "/simple-member.ts": /* ts */ `
+ enum x { y = 123 }
+ console.log(x.y)
+ `,
+ "/simple-enum.ts": /* ts */ `
+ enum x { y = 123 }
+ console.log(x)
+ `,
+ "/sibling-member.ts": /* ts */ `
+ enum x { y = 123 }
+ enum x { z = y * 2 }
+ console.log(x.y, x.z)
+ `,
+ "/sibling-enum-before.ts": /* ts */ `
+ console.log(x)
+ enum x { y = 123 }
+ enum x { z = y * 2 }
+ `,
+ "/sibling-enum-middle.ts": /* ts */ `
+ enum x { y = 123 }
+ console.log(x)
+ enum x { z = y * 2 }
+ `,
+ "/sibling-enum-after.ts": /* ts */ `
+ enum x { y = 123 }
+ enum x { z = y * 2 }
+ console.log(x)
+ `,
+ "/namespace-before.ts": /* ts */ `
+ namespace x { console.log(x, y) }
+ enum x { y = 123 }
+ `,
+ "/namespace-after.ts": /* ts */ `
+ enum x { y = 123 }
+ namespace x { console.log(x, y) }
+ `,
+ },
+ entryPoints: [
+ "/simple-member.ts",
+ "/simple-enum.ts",
+ "/sibling-member.ts",
+ "/sibling-enum-before.ts",
+ "/sibling-enum-middle.ts",
+ "/sibling-enum-after.ts",
+ "/namespace-before.ts",
+ "/namespace-after.ts",
+ ],
+ });
+ itBundled("ts/TSEnumJSX", {
+ // GENERATED
+ files: {
+ "/element.tsx": /* tsx */ `
+ export enum Foo { Div = 'div' }
+ console.log(<Foo.Div />)
+ `,
+ "/fragment.tsx": /* tsx */ `
+ export enum React { Fragment = 'div' }
+ console.log(<>test</>)
+ `,
+ "/nested-element.tsx": /* tsx */ `
+ namespace x.y { export enum Foo { Div = 'div' } }
+ namespace x.y { console.log(<x.y.Foo.Div />) }
+ `,
+ "/nested-fragment.tsx": /* tsx */ `
+ namespace x.y { export enum React { Fragment = 'div' } }
+ namespace x.y { console.log(<>test</>) }
+ `,
+ },
+ entryPoints: ["/element.tsx", "/fragment.tsx", "/nested-element.tsx", "/nested-fragment.tsx"],
+ mode: "passthrough",
+ });
+ itBundled("ts/TSEnumDefine", {
+ // GENERATED
+ files: {
+ "/entry.ts": `enum a { b = 123, c = d }`,
+ },
+ mode: "passthrough",
+ });
+ itBundled("ts/TSEnumSameModuleInliningAccess", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ enum a { x = 123 }
+ enum b { x = 123 }
+ enum c { x = 123 }
+ enum d { x = 123 }
+ enum e { x = 123 }
+ console.log([
+ a.x,
+ b['x'],
+ c?.x,
+ d?.['x'],
+ e,
+ ])
+ `,
+ },
+ });
+ itBundled("ts/TSEnumCrossModuleInliningAccess", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import { a, b, c, d, e } from './enums'
+ console.log([
+ a.x,
+ b['x'],
+ c?.x,
+ d?.['x'],
+ e,
+ ])
+ `,
+ "/enums.ts": /* ts */ `
+ export enum a { x = 123 }
+ export enum b { x = 123 }
+ export enum c { x = 123 }
+ export enum d { x = 123 }
+ export enum e { x = 123 }
+ `,
+ },
+ });
+ itBundled("ts/TSEnumCrossModuleInliningDefinitions", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import { a } from './enums'
+ console.log([
+ a.implicit_number,
+ a.explicit_number,
+ a.explicit_string,
+ a.non_constant,
+ ])
+ `,
+ "/enums.ts": /* ts */ `
+ export enum a {
+ implicit_number,
+ explicit_number = 123,
+ explicit_string = 'xyz',
+ non_constant = foo,
+ }
+ `,
+ },
+ });
+ itBundled("ts/TSEnumCrossModuleInliningReExport", {
+ // GENERATED
+ files: {
+ "/entry.js": /* js */ `
+ import { a } from './re-export'
+ import { b } from './re-export-star'
+ import * as ns from './enums'
+ console.log([
+ a.x,
+ b.x,
+ ns.c.x,
+ ])
+ `,
+ "/re-export.js": `export { a } from './enums'`,
+ "/re-export-star.js": `export * from './enums'`,
+ "/enums.ts": /* ts */ `
+ export enum a { x = 'a' }
+ export enum b { x = 'b' }
+ export enum c { x = 'c' }
+ `,
+ },
+ });
+ itBundled("ts/TSEnumCrossModuleTreeShaking", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import {
+ a_DROP,
+ b_DROP,
+ c_DROP,
+ } from './enums'
+
+ console.log([
+ a_DROP.x,
+ b_DROP['x'],
+ c_DROP.x,
+ ])
+
+ import {
+ a_keep,
+ b_keep,
+ c_keep,
+ d_keep,
+ e_keep,
+ } from './enums'
+
+ console.log([
+ a_keep.x,
+ b_keep.x,
+ c_keep,
+ d_keep.y,
+ e_keep.x,
+ ])
+ `,
+ "/enums.ts": /* ts */ `
+ export enum a_DROP { x = 1 } // test a dot access
+ export enum b_DROP { x = 2 } // test an index access
+ export enum c_DROP { x = '' } // test a string enum
+
+ export enum a_keep { x = false } // false is not inlinable
+ export enum b_keep { x = foo } // foo has side effects
+ export enum c_keep { x = 3 } // this enum object is captured
+ export enum d_keep { x = 4 } // we access "y" on this object
+ export let e_keep = {} // non-enum properties should be kept
+ `,
+ },
+ });
+ itBundled("ts/TSEnumExportClause", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ import {
+ A,
+ B,
+ C as c,
+ d as dd,
+ } from './enums'
+
+ console.log([
+ A.A,
+ B.B,
+ c.C,
+ dd.D,
+ ])
+ `,
+ "/enums.ts": /* ts */ `
+ export enum A { A = 1 }
+ enum B { B = 2 }
+ export enum C { C = 3 }
+ enum D { D = 4 }
+ export { B, D as d }
+ `,
+ },
+ });
+ itBundled("ts/TSThisIsUndefinedWarning", {
+ // GENERATED
+ files: {
+ "/warning1.ts": `export var foo = this`,
+ "/warning2.ts": `export var foo = this || this.foo`,
+ "/warning3.ts": `export var foo = this ? this.foo : null`,
+ "/silent1.ts": `export var foo = this && this.foo`,
+ "/silent2.ts": `export var foo = this && (() => this.foo)`,
+ },
+ entryPoints: ["/warning1.ts", "/warning2.ts", "/warning3.ts", "/silent1.ts", "/silent2.ts"],
+ /* TODO FIX expectedScanLog: `warning1.ts: DEBUG: Top-level "this" will be replaced with undefined since this file is an ECMAScript module
+ warning1.ts: NOTE: This file is considered to be an ECMAScript module because of the "export" keyword here:
+ warning2.ts: DEBUG: Top-level "this" will be replaced with undefined since this file is an ECMAScript module
+ warning2.ts: NOTE: This file is considered to be an ECMAScript module because of the "export" keyword here:
+ warning3.ts: DEBUG: Top-level "this" will be replaced with undefined since this file is an ECMAScript module
+ warning3.ts: NOTE: This file is considered to be an ECMAScript module because of the "export" keyword here:
+ `, */
+ });
+ itBundled("ts/TSCommonJSVariableInESMTypeModule", {
+ // GENERATED
+ files: {
+ "/entry.ts": `module.exports = null`,
+ "/package.json": `{ "type": "module" }`,
+ },
+ /* TODO FIX expectedScanLog: `entry.ts: WARNING: The CommonJS "module" variable is treated as a global variable in an ECMAScript module and may not work as expected
+ package.json: NOTE: This file is considered to be an ECMAScript module because the enclosing "package.json" file sets the type of this file to "module":
+ NOTE: Node's package format requires that CommonJS files in a "type": "module" package use the ".cjs" file extension. If you are using TypeScript, you can use the ".cts" file extension with esbuild instead.
+ `, */
+ });
+ itBundled("ts/EnumRulesFrom_TypeScript_5_0", {
+ // GENERATED
+ files: {
+ "/supported.ts": /* ts */ `
+ // From https://github.com/microsoft/TypeScript/pull/50528:
+ // "An expression is considered a constant expression if it is
+ const enum Foo {
+ // a number or string literal,
+ X0 = 123,
+ X1 = 'x',
+
+ // a unary +, -, or ~ applied to a numeric constant expression,
+ X2 = +1,
+ X3 = -2,
+ X4 = ~3,
+
+ // a binary +, -, *, /, %, **, <<, >>, >>>, |, &, ^ applied to two numeric constant expressions,
+ X5 = 1 + 2,
+ X6 = 1 - 2,
+ X7 = 2 * 3,
+ X8 = 1 / 2,
+ X9 = 3 % 2,
+ X10 = 2 ** 3,
+ X11 = 1 << 2,
+ X12 = -9 >> 1,
+ X13 = -9 >>> 1,
+ X14 = 5 | 12,
+ X15 = 5 & 12,
+ X16 = 5 ^ 12,
+
+ // a binary + applied to two constant expressions whereof at least one is a string,
+ X17 = 'x' + 0,
+ X18 = 0 + 'x',
+ X19 = 'x' + 'y',
+ X20 = '' + NaN,
+ X21 = '' + Infinity,
+ X22 = '' + -Infinity,
+ X23 = '' + -0,
+
+ // a template expression where each substitution expression is a constant expression,
+ X24 = \` + "\`A\$00}B\$0'x'}C\$01 + 3 - 4 / 2 * 5 ** 6}D\`" +
+ `,
+ "/not-supported.ts": /* ts */ `
+ const enum NonIntegerNumberToString {
+ SUPPORTED = '' + 1,
+ UNSUPPORTED = '' + 1.5,
+ }
+ console.log(
+ NonIntegerNumberToString.SUPPORTED,
+ NonIntegerNumberToString.UNSUPPORTED,
+ )
+
+ const enum OutOfBoundsNumberToString {
+ SUPPORTED = '' + 1_000_000_000,
+ UNSUPPORTED = '' + 1_000_000_000_000,
+ }
+ console.log(
+ OutOfBoundsNumberToString.SUPPORTED,
+ OutOfBoundsNumberToString.UNSUPPORTED,
+ )
+
+ const enum TemplateExpressions {
+ // TypeScript enums don't handle any of these
+ NULL = '' + null,
+ TRUE = '' + true,
+ FALSE = '' + false,
+ BIGINT = '' + 123n,
+ }
+ console.log(
+ TemplateExpressions.NULL,
+ TemplateExpressions.TRUE,
+ TemplateExpressions.FALSE,
+ TemplateExpressions.BIGINT,
+ )
+ `,
+ },
+ entryPoints: ["/supported.ts", "/not-supported.ts"],
+ });
+ itBundled("ts/TSEnumUseBeforeDeclare", {
+ // GENERATED
+ files: {
+ "/entry.ts": /* ts */ `
+ export function before() {
+ console.log(Foo.FOO)
+ }
+ enum Foo { FOO }
+ export function after() {
+ console.log(Foo.FOO)
+ }
+ `,
+ },
+ });
+});
diff --git a/test/bundler/esbuild/tsconfig.test.ts b/test/bundler/esbuild/tsconfig.test.ts
new file mode 100644
index 000000000..8c83fee7d
--- /dev/null
+++ b/test/bundler/esbuild/tsconfig.test.ts
@@ -0,0 +1,1565 @@
+import { bundlerTest, expectBundled, itBundled, testForFile } from "../expectBundled";
+var { describe, test, expect } = testForFile(import.meta.path);
+
+// Tests ported from:
+// https://github.com/evanw/esbuild/blob/main/internal/bundler_tests/bundler_tsconfig_test.go
+
+// For debug, all files are written to $TEMP/bun-bundle-tests/tsconfig
+
+describe("bundler", () => {
+ return;
+ itBundled("tsconfig/TsConfigPaths", {
+ // GENERATED
+ files: {
+ "/Users/user/project/entry.ts": /* ts */ `
+ import baseurl_dot from './baseurl_dot'
+ import baseurl_nested from './baseurl_nested'
+ console.log(baseurl_dot, baseurl_nested)
+ `,
+ "/Users/user/project/baseurl_dot/index.ts": /* ts */ `
+ import test0 from 'test0'
+ import test1 from 'test1/foo'
+ import test2 from 'test2/foo'
+ import test3 from 'test3/foo'
+ import test4 from 'test4/foo'
+ import test5 from 'test5/foo'
+ import absoluteIn from './absolute-in'
+ import absoluteInStar from './absolute-in-star'
+ import absoluteOut from './absolute-out'
+ import absoluteOutStar from './absolute-out-star'
+ export default {
+ test0,
+ test1,
+ test2,
+ test3,
+ test4,
+ test5,
+ absoluteIn,
+ absoluteInStar,
+ absoluteOut,
+ absoluteOutStar,
+ }
+ `,
+ "/Users/user/project/baseurl_dot/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "test0": ["./test0-success.ts"],
+ "test1/*": ["./test1-success.ts"],
+ "test2/*": ["./test2-success/*"],
+ "t*t3/foo": ["./test3-succ*s.ts"],
+ "test4/*": ["./test4-first/*", "./test4-second/*"],
+ "test5/*": ["./test5-first/*", "./test5-second/*"],
+ "/virtual-in/test": ["./actual/test"],
+ "/virtual-in-star/*": ["./actual/*"],
+ "/virtual-out/test": ["/Users/user/project/baseurl_dot/actual/test"],
+ "/virtual-out-star/*": ["/Users/user/project/baseurl_dot/actual/*"],
+ }
+ }
+ }
+ `,
+ "/Users/user/project/baseurl_dot/test0-success.ts": `export default 'test0-success'`,
+ "/Users/user/project/baseurl_dot/test1-success.ts": `export default 'test1-success'`,
+ "/Users/user/project/baseurl_dot/test2-success/foo.ts": `export default 'test2-success'`,
+ "/Users/user/project/baseurl_dot/test3-success.ts": `export default 'test3-success'`,
+ "/Users/user/project/baseurl_dot/test4-first/foo.ts": `export default 'test4-success'`,
+ "/Users/user/project/baseurl_dot/test5-second/foo.ts": `export default 'test5-success'`,
+ "/Users/user/project/baseurl_dot/absolute-in.ts": `export {default} from '/virtual-in/test'`,
+ "/Users/user/project/baseurl_dot/absolute-in-star.ts": `export {default} from '/virtual-in-star/test'`,
+ "/Users/user/project/baseurl_dot/absolute-out.ts": `export {default} from '/virtual-out/test'`,
+ "/Users/user/project/baseurl_dot/absolute-out-star.ts": `export {default} from '/virtual-out-star/test'`,
+ "/Users/user/project/baseurl_dot/actual/test.ts": `export default 'absolute-success'`,
+ "/Users/user/project/baseurl_nested/index.ts": /* ts */ `
+ import test0 from 'test0'
+ import test1 from 'test1/foo'
+ import test2 from 'test2/foo'
+ import test3 from 'test3/foo'
+ import test4 from 'test4/foo'
+ import test5 from 'test5/foo'
+ import absoluteIn from './absolute-in'
+ import absoluteInStar from './absolute-in-star'
+ import absoluteOut from './absolute-out'
+ import absoluteOutStar from './absolute-out-star'
+ export default {
+ test0,
+ test1,
+ test2,
+ test3,
+ test4,
+ test5,
+ absoluteIn,
+ absoluteInStar,
+ absoluteOut,
+ absoluteOutStar,
+ }
+ `,
+ "/Users/user/project/baseurl_nested/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "baseUrl": "nested",
+ "paths": {
+ "test0": ["./test0-success.ts"],
+ "test1/*": ["./test1-success.ts"],
+ "test2/*": ["./test2-success/*"],
+ "t*t3/foo": ["./test3-succ*s.ts"],
+ "test4/*": ["./test4-first/*", "./test4-second/*"],
+ "test5/*": ["./test5-first/*", "./test5-second/*"],
+ "/virtual-in/test": ["./actual/test"],
+ "/virtual-in-star/*": ["./actual/*"],
+ "/virtual-out/test": ["/Users/user/project/baseurl_nested/nested/actual/test"],
+ "/virtual-out-star/*": ["/Users/user/project/baseurl_nested/nested/actual/*"],
+ }
+ }
+ }
+ `,
+ "/Users/user/project/baseurl_nested/nested/test0-success.ts": `export default 'test0-success'`,
+ "/Users/user/project/baseurl_nested/nested/test1-success.ts": `export default 'test1-success'`,
+ "/Users/user/project/baseurl_nested/nested/test2-success/foo.ts": `export default 'test2-success'`,
+ "/Users/user/project/baseurl_nested/nested/test3-success.ts": `export default 'test3-success'`,
+ "/Users/user/project/baseurl_nested/nested/test4-first/foo.ts": `export default 'test4-success'`,
+ "/Users/user/project/baseurl_nested/nested/test5-second/foo.ts": `export default 'test5-success'`,
+ "/Users/user/project/baseurl_nested/absolute-in.ts": `export {default} from '/virtual-in/test'`,
+ "/Users/user/project/baseurl_nested/absolute-in-star.ts": `export {default} from '/virtual-in/test'`,
+ "/Users/user/project/baseurl_nested/absolute-out.ts": `export {default} from '/virtual-out/test'`,
+ "/Users/user/project/baseurl_nested/absolute-out-star.ts": `export {default} from '/virtual-out-star/test'`,
+ "/Users/user/project/baseurl_nested/nested/actual/test.ts": `export default 'absolute-success'`,
+ },
+ });
+ itBundled("tsconfig/TsConfigPathsNoBaseURL", {
+ // GENERATED
+ files: {
+ "/Users/user/project/entry.ts": /* ts */ `
+ import simple from './simple'
+ import extended from './extended'
+ console.log(simple, extended)
+ `,
+ "/Users/user/project/simple/index.ts": /* ts */ `
+ import test0 from 'test0'
+ import test1 from 'test1/foo'
+ import test2 from 'test2/foo'
+ import test3 from 'test3/foo'
+ import test4 from 'test4/foo'
+ import test5 from 'test5/foo'
+ import absolute from './absolute'
+ export default {
+ test0,
+ test1,
+ test2,
+ test3,
+ test4,
+ test5,
+ absolute,
+ }
+ `,
+ "/Users/user/project/simple/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "paths": {
+ "test0": ["./test0-success.ts"],
+ "test1/*": ["./test1-success.ts"],
+ "test2/*": ["./test2-success/*"],
+ "t*t3/foo": ["./test3-succ*s.ts"],
+ "test4/*": ["./test4-first/*", "./test4-second/*"],
+ "test5/*": ["./test5-first/*", "./test5-second/*"],
+ "/virtual/*": ["./actual/*"],
+ }
+ }
+ }
+ `,
+ "/Users/user/project/simple/test0-success.ts": `export default 'test0-success'`,
+ "/Users/user/project/simple/test1-success.ts": `export default 'test1-success'`,
+ "/Users/user/project/simple/test2-success/foo.ts": `export default 'test2-success'`,
+ "/Users/user/project/simple/test3-success.ts": `export default 'test3-success'`,
+ "/Users/user/project/simple/test4-first/foo.ts": `export default 'test4-success'`,
+ "/Users/user/project/simple/test5-second/foo.ts": `export default 'test5-success'`,
+ "/Users/user/project/simple/absolute.ts": `export {default} from '/virtual/test'`,
+ "/Users/user/project/simple/actual/test.ts": `export default 'absolute-success'`,
+ "/Users/user/project/extended/index.ts": /* ts */ `
+ import test0 from 'test0'
+ import test1 from 'test1/foo'
+ import test2 from 'test2/foo'
+ import test3 from 'test3/foo'
+ import test4 from 'test4/foo'
+ import test5 from 'test5/foo'
+ import absolute from './absolute'
+ export default {
+ test0,
+ test1,
+ test2,
+ test3,
+ test4,
+ test5,
+ absolute,
+ }
+ `,
+ "/Users/user/project/extended/tsconfig.json": /* json */ `
+ {
+ "extends": "./nested/tsconfig.json"
+ }
+ `,
+ "/Users/user/project/extended/nested/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "paths": {
+ "test0": ["./test0-success.ts"],
+ "test1/*": ["./test1-success.ts"],
+ "test2/*": ["./test2-success/*"],
+ "t*t3/foo": ["./test3-succ*s.ts"],
+ "test4/*": ["./test4-first/*", "./test4-second/*"],
+ "test5/*": ["./test5-first/*", "./test5-second/*"],
+ "/virtual/*": ["./actual/*"],
+ }
+ }
+ }
+ `,
+ "/Users/user/project/extended/nested/test0-success.ts": `export default 'test0-success'`,
+ "/Users/user/project/extended/nested/test1-success.ts": `export default 'test1-success'`,
+ "/Users/user/project/extended/nested/test2-success/foo.ts": `export default 'test2-success'`,
+ "/Users/user/project/extended/nested/test3-success.ts": `export default 'test3-success'`,
+ "/Users/user/project/extended/nested/test4-first/foo.ts": `export default 'test4-success'`,
+ "/Users/user/project/extended/nested/test5-second/foo.ts": `export default 'test5-success'`,
+ "/Users/user/project/extended/absolute.ts": `export {default} from '/virtual/test'`,
+ "/Users/user/project/extended/nested/actual/test.ts": `export default 'absolute-success'`,
+ },
+ });
+ itBundled("tsconfig/TsConfigBadPathsNoBaseURL", {
+ // GENERATED
+ files: {
+ "/Users/user/project/entry.ts": `import "should-not-be-imported"`,
+ "/Users/user/project/should-not-be-imported.ts": ``,
+ "/Users/user/project/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "paths": {
+ "test": [
+ ".",
+ "..",
+ "./good",
+ ".\\good",
+ "../good",
+ "..\\good",
+ "/good",
+ "\\good",
+ "c:/good",
+ "c:\\good",
+ "C:/good",
+ "C:\\good",
+
+ "bad",
+ "@bad/core",
+ ".*/bad",
+ "..*/bad",
+ "c*:\\bad",
+ "c:*\\bad",
+ "http://bad"
+ ]
+ }
+ }
+ }
+ `,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/entry.ts: ERROR: Could not resolve "should-not-be-imported"
+ NOTE: Use the relative path "./should-not-be-imported" to reference the file "Users/user/project/should-not-be-imported.ts". Without the leading "./", the path "should-not-be-imported" is being interpreted as a package path instead.
+ Users/user/project/tsconfig.json: WARNING: Non-relative path "bad" is not allowed when "baseUrl" is not set (did you forget a leading "./"?)
+ Users/user/project/tsconfig.json: WARNING: Non-relative path "@bad/core" is not allowed when "baseUrl" is not set (did you forget a leading "./"?)
+ Users/user/project/tsconfig.json: WARNING: Non-relative path ".* /bad" is not allowed when "baseUrl" is not set (did you forget a leading "./"?)
+ Users/user/project/tsconfig.json: WARNING: Non-relative path "..* /bad" is not allowed when "baseUrl" is not set (did you forget a leading "./"?)
+ Users/user/project/tsconfig.json: WARNING: Non-relative path "c*:\\bad" is not allowed when "baseUrl" is not set (did you forget a leading "./"?)
+ Users/user/project/tsconfig.json: WARNING: Non-relative path "c:*\\bad" is not allowed when "baseUrl" is not set (did you forget a leading "./"?)
+ Users/user/project/tsconfig.json: WARNING: Non-relative path "http://bad" is not allowed when "baseUrl" is not set (did you forget a leading "./"?)
+ `, */
+ });
+ itBundled("tsconfig/TsConfigPathsOverriddenBaseURL", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.ts": /* ts */ `
+ import test from '#/test'
+ console.log(test)
+ `,
+ "/Users/user/project/src/test.ts": `export default 123`,
+ "/Users/user/project/tsconfig.json": /* json */ `
+ {
+ "extends": "./tsconfig.paths.json",
+ "compilerOptions": {
+ "baseUrl": "./src"
+ }
+ }
+ `,
+ "/Users/user/project/tsconfig.paths.json": /* json */ `
+ {
+ "compilerOptions": {
+ "paths": {
+ "#/*": ["./*"]
+ }
+ }
+ }
+ `,
+ },
+ });
+ itBundled("tsconfig/TsConfigPathsOverriddenBaseURLDifferentDir", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.ts": /* ts */ `
+ import test from '#/test'
+ console.log(test)
+ `,
+ "/Users/user/project/src/test.ts": `export default 123`,
+ "/Users/user/project/src/tsconfig.json": /* json */ `
+ {
+ "extends": "../tsconfig.paths.json",
+ "compilerOptions": {
+ "baseUrl": "./"
+ }
+ }
+ `,
+ "/Users/user/project/tsconfig.paths.json": /* json */ `
+ {
+ "compilerOptions": {
+ "paths": {
+ "#/*": ["./*"]
+ }
+ }
+ }
+ `,
+ },
+ });
+ itBundled("tsconfig/TsConfigPathsMissingBaseURL", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.ts": /* ts */ `
+ import test from '#/test'
+ console.log(test)
+ `,
+ "/Users/user/project/src/test.ts": `export default 123`,
+ "/Users/user/project/src/tsconfig.json": /* json */ `
+ {
+ "extends": "../tsconfig.paths.json",
+ "compilerOptions": {
+ }
+ }
+ `,
+ "/Users/user/project/tsconfig.paths.json": /* json */ `
+ {
+ "compilerOptions": {
+ "paths": {
+ "#/*": ["./*"]
+ }
+ }
+ }
+ `,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/src/entry.ts: ERROR: Could not resolve "#/test"
+ NOTE: You can mark the path "#/test" as external to exclude it from the bundle, which will remove this error.
+ `, */
+ });
+ itBundled("tsconfig/TsConfigPathsTypeOnly", {
+ // GENERATED
+ files: {
+ "/Users/user/project/entry.ts": /* ts */ `
+ import { fib } from "fib";
+
+ console.log(fib(10));
+ `,
+ "/Users/user/project/node_modules/fib/index.js": /* js */ `
+ export function fib(input) {
+ if (input < 2) {
+ return input;
+ }
+ return fib(input - 1) + fib(input - 2);
+ }
+ `,
+ "/Users/user/project/fib-local.d.ts": `export function fib(input: number): number;`,
+ "/Users/user/project/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "fib": ["fib-local.d.ts"]
+ }
+ }
+ }
+ `,
+ },
+ });
+ itBundled("tsconfig/TsConfigJSX", {
+ // GENERATED
+ files: {
+ "/Users/user/project/entry.tsx": `console.log(<><div/><div/></>)`,
+ "/Users/user/project/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "jsxFactory": "R.c",
+ "jsxFragmentFactory": "R.F"
+ }
+ }
+ `,
+ },
+ });
+ itBundled("tsconfig/TsConfigNestedJSX", {
+ // GENERATED
+ files: {
+ "/Users/user/project/entry.ts": /* ts */ `
+ import factory from './factory'
+ import fragment from './fragment'
+ import both from './both'
+ console.log(factory, fragment, both)
+ `,
+ "/Users/user/project/factory/index.tsx": `export default <><div/><div/></>`,
+ "/Users/user/project/factory/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "jsxFactory": "h"
+ }
+ }
+ `,
+ "/Users/user/project/fragment/index.tsx": `export default <><div/><div/></>`,
+ "/Users/user/project/fragment/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "jsxFragmentFactory": "a.b"
+ }
+ }
+ `,
+ "/Users/user/project/both/index.tsx": `export default <><div/><div/></>`,
+ "/Users/user/project/both/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "jsxFactory": "R.c",
+ "jsxFragmentFactory": "R.F"
+ }
+ }
+ `,
+ },
+ });
+ itBundled("tsconfig/TsConfigReactJSX", {
+ // GENERATED
+ files: {
+ "/Users/user/project/entry.tsx": `console.log(<><div/><div/></>)`,
+ "/Users/user/project/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "jsx": "react-jsx",
+ "jsxImportSource": "notreact"
+ }
+ }
+ `,
+ },
+ outfile: "/Users/user/project/out.js",
+ });
+ itBundled("tsconfig/TsConfigReactJSXDev", {
+ // GENERATED
+ files: {
+ "/Users/user/project/entry.tsx": `console.log(<><div/><div/></>)`,
+ "/Users/user/project/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "jsx": "react-jsxdev"
+ }
+ }
+ `,
+ },
+ outfile: "/Users/user/project/out.js",
+ });
+ itBundled("tsconfig/TsConfigReactJSXWithDevInMainConfig", {
+ // GENERATED
+ files: {
+ "/Users/user/project/entry.tsx": `console.log(<><div/><div/></>)`,
+ "/Users/user/project/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "jsx": "react-jsx"
+ }
+ }
+ `,
+ },
+ outfile: "/Users/user/project/out.js",
+ jsx: {
+ development: true,
+ },
+ });
+ itBundled("tsconfig/TsconfigJsonBaseUrl", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/app/entry.js": /* js */ `
+ import fn from 'lib/util'
+ console.log(fn())
+ `,
+ "/Users/user/project/src/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "baseUrl": "."
+ }
+ }
+ `,
+ "/Users/user/project/src/lib/util.js": /* js */ `
+ module.exports = function() {
+ return 123
+ }
+ `,
+ },
+ });
+ itBundled("tsconfig/JsconfigJsonBaseUrl", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/app/entry.js": /* js */ `
+ import fn from 'lib/util'
+ console.log(fn())
+ `,
+ "/Users/user/project/src/jsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "baseUrl": "."
+ }
+ }
+ `,
+ "/Users/user/project/src/lib/util.js": /* js */ `
+ module.exports = function() {
+ return 123
+ }
+ `,
+ },
+ });
+ itBundled("tsconfig/TsconfigJsonAbsoluteBaseUrl", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/app/entry.js": /* js */ `
+ import fn from 'lib/util'
+ console.log(fn())
+ `,
+ "/Users/user/project/src/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "baseUrl": "/Users/user/project/src"
+ }
+ }
+ `,
+ "/Users/user/project/src/lib/util.js": /* js */ `
+ module.exports = function() {
+ return 123
+ }
+ `,
+ },
+ });
+ itBundled("tsconfig/TsconfigJsonCommentAllowed", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/app/entry.js": /* js */ `
+ import fn from 'lib/util'
+ console.log(fn())
+ `,
+ "/Users/user/project/src/tsconfig.json": /* json */ `
+ {
+ // Single-line comment
+ "compilerOptions": {
+ "baseUrl": "."
+ }
+ }
+ `,
+ "/Users/user/project/src/lib/util.js": /* js */ `
+ module.exports = function() {
+ return 123
+ }
+ `,
+ },
+ });
+ itBundled("tsconfig/TsconfigJsonTrailingCommaAllowed", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/app/entry.js": /* js */ `
+ import fn from 'lib/util'
+ console.log(fn())
+ `,
+ "/Users/user/project/src/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "baseUrl": ".",
+ },
+ }
+ `,
+ "/Users/user/project/src/lib/util.js": /* js */ `
+ module.exports = function() {
+ return 123
+ }
+ `,
+ },
+ });
+ itBundled("tsconfig/TsconfigJsonExtends", {
+ // GENERATED
+ files: {
+ "/entry.jsx": `console.log(<div/>, <></>)`,
+ "/tsconfig.json": /* json */ `
+ {
+ "extends": "./base",
+ "compilerOptions": {
+ "jsxFragmentFactory": "derivedFragment"
+ }
+ }
+ `,
+ "/base.json": /* json */ `
+ {
+ "compilerOptions": {
+ "jsxFactory": "baseFactory",
+ "jsxFragmentFactory": "baseFragment"
+ }
+ }
+ `,
+ },
+ });
+ bundlerTest.skip("tsconfig/TsconfigJsonExtendsAbsolute", () => {
+ expectBundled("tsconfig/TsconfigJsonExtendsAbsoluteUnix", {
+ // GENERATED
+ host: "unix",
+ files: {
+ "/Users/user/project/entry.jsx": `console.log(<div/>, <></>)`,
+ "/Users/user/project/tsconfig.json": /* json */ `
+ {
+ "extends": "/Users/user/project/base.json",
+ "compilerOptions": {
+ "jsxFragmentFactory": "derivedFragment"
+ }
+ }
+ `,
+ "/Users/user/project/base.json": /* json */ `
+ {
+ "compilerOptions": {
+ "jsxFactory": "baseFactory",
+ "jsxFragmentFactory": "baseFragment"
+ }
+ }
+ `,
+ },
+ });
+ expectBundled("tsconfig/TsconfigJsonExtendsAbsoluteWindows", {
+ // GENERATED
+ host: "windows",
+ files: {
+ "/Users/user/project/entry.jsx": `console.log(<div/>, <></>)`,
+ "/Users/user/project/tsconfig.json": /* json */ `
+ {
+ "extends": "C:\\Users\\user\\project\\base.json",
+ "compilerOptions": {
+ "jsxFragmentFactory": "derivedFragment"
+ }
+ }
+ `,
+ "/Users/user/project/base.json": /* json */ `
+ {
+ "compilerOptions": {
+ "jsxFactory": "baseFactory",
+ "jsxFragmentFactory": "baseFragment"
+ }
+ }
+ `,
+ },
+ });
+ });
+ itBundled("tsconfig/TsconfigJsonExtendsThreeLevels", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.jsx": /* jsx */ `
+ import "test/import.js"
+ console.log(<div/>, <></>)
+ `,
+ "/Users/user/project/src/tsconfig.json": /* json */ `
+ {
+ "extends": "./path1/base",
+ "compilerOptions": {
+ "jsxFragmentFactory": "derivedFragment"
+ }
+ }
+ `,
+ "/Users/user/project/src/path1/base.json": /* json */ `
+ {
+ "extends": "../path2/base2"
+ }
+ `,
+ "/Users/user/project/src/path2/base2.json": /* json */ `
+ {
+ "compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "test/*": ["./works/*"]
+ },
+ "jsxFactory": "baseFactory",
+ "jsxFragmentFactory": "baseFragment"
+ }
+ }
+ `,
+ "/Users/user/project/src/path2/works/import.js": `console.log('works')`,
+ },
+ });
+ itBundled("tsconfig/TsconfigJsonExtendsLoop", {
+ // GENERATED
+ files: {
+ "/entry.js": `console.log(123)`,
+ "/tsconfig.json": /* json */ `
+ {
+ "extends": "./base.json"
+ }
+ `,
+ "/base.json": /* json */ `
+ {
+ "extends": "./tsconfig"
+ }
+ `,
+ },
+ /* TODO FIX expectedScanLog: `base.json: WARNING: Base config file "./tsconfig" forms cycle
+ `, */
+ });
+ itBundled("tsconfig/TsconfigJsonExtendsPackage", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/app/entry.jsx": `console.log(<div/>)`,
+ "/Users/user/project/src/tsconfig.json": /* json */ `
+ {
+ "extends": "@package/foo/tsconfig.json"
+ }
+ `,
+ "/Users/user/project/node_modules/@package/foo/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "jsxFactory": "worked"
+ }
+ }
+ `,
+ },
+ });
+ itBundled("tsconfig/TsconfigJsonOverrideMissing", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/app/entry.ts": `import 'foo'`,
+ "/Users/user/project/src/foo-bad.ts": `console.log('bad')`,
+ "/Users/user/project/src/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "foo": ["./foo-bad.ts"]
+ }
+ }
+ }
+ `,
+ "/Users/user/project/other/foo-good.ts": `console.log('good')`,
+ "/Users/user/project/other/config-for-ts.json": /* json */ `
+ {
+ "compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "foo": ["./foo-good.ts"]
+ }
+ }
+ }
+ `,
+ },
+ outfile: "/Users/user/project/out.js",
+ });
+ itBundled("tsconfig/TsconfigJsonOverrideNodeModules", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/app/entry.ts": `import 'foo'`,
+ "/Users/user/project/src/node_modules/foo/index.js": `console.log('default')`,
+ "/Users/user/project/src/foo-bad.ts": `console.log('bad')`,
+ "/Users/user/project/src/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "foo": ["./foo-bad.ts"]
+ }
+ }
+ }
+ `,
+ "/Users/user/project/other/foo-good.ts": `console.log('good')`,
+ "/Users/user/project/other/config-for-ts.json": /* json */ `
+ {
+ "compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "foo": ["./foo-good.ts"]
+ }
+ }
+ }
+ `,
+ },
+ outfile: "/Users/user/project/out.js",
+ });
+ itBundled("tsconfig/TsconfigJsonOverrideInvalid", {
+ // GENERATED
+ files: {
+ "/entry.ts": ``,
+ },
+ /* TODO FIX expectedScanLog: `ERROR: Cannot find tsconfig file "this/file/doesn't/exist/tsconfig.json"
+ `, */
+ });
+ itBundled("tsconfig/TsconfigJsonNodeModulesImplicitFile", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/app/entry.tsx": `console.log(<div/>)`,
+ "/Users/user/project/src/tsconfig.json": /* json */ `
+ {
+ "extends": "foo"
+ }
+ `,
+ "/Users/user/project/src/node_modules/foo/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "jsx": "react",
+ "jsxFactory": "worked"
+ }
+ }
+ `,
+ },
+ });
+ itBundled("tsconfig/TsconfigJsonInsideNodeModules", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/app/entry.tsx": `import 'foo'`,
+ "/Users/user/project/src/node_modules/foo/index.tsx": `console.log(<div/>)`,
+ "/Users/user/project/src/node_modules/foo/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "jsxFactory": "TEST_FAILED"
+ }
+ }
+ `,
+ },
+ });
+ itBundled("tsconfig/TsconfigWarningsInsideNodeModules", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.tsx": /* tsx */ `
+ import "./foo"
+ import "bar"
+ `,
+ "/Users/user/project/src/foo/tsconfig.json": `{ "extends": "extends for foo" }`,
+ "/Users/user/project/src/foo/index.js": ``,
+ "/Users/user/project/src/node_modules/bar/tsconfig.json": `{ "extends": "extends for bar" }`,
+ "/Users/user/project/src/node_modules/bar/index.js": ``,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/src/foo/tsconfig.json: WARNING: Cannot find base config file "extends for foo"
+ `, */
+ });
+ itBundled("tsconfig/TsconfigRemoveUnusedImports", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.ts": /* ts */ `
+ import {x, y} from "./foo"
+ console.log(1 as x)
+ `,
+ "/Users/user/project/src/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "importsNotUsedAsValues": "remove"
+ }
+ }
+ `,
+ },
+ });
+ itBundled("tsconfig/TsconfigPreserveUnusedImports", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.ts": /* ts */ `
+ import {x, y} from "./foo"
+ console.log(1 as x)
+ `,
+ "/Users/user/project/src/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "importsNotUsedAsValues": "preserve"
+ }
+ }
+ `,
+ },
+ outfile: "/Users/user/project/out.js",
+ });
+ itBundled("tsconfig/TsconfigImportsNotUsedAsValuesPreserve", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.ts": /* ts */ `
+ import {x, y} from "./foo"
+ import z from "./foo"
+ import * as ns from "./foo"
+ console.log(1 as x, 2 as z, 3 as ns.y)
+ `,
+ "/Users/user/project/src/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "importsNotUsedAsValues": "preserve"
+ }
+ }
+ `,
+ },
+ format: "esm",
+ outfile: "/Users/user/project/out.js",
+ mode: "convertformat",
+ });
+ itBundled("tsconfig/TsconfigPreserveValueImports", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.ts": /* ts */ `
+ import {} from "a"
+ import {b1} from "b"
+ import {c1, type c2} from "c"
+ import {d1, d2, type d3} from "d"
+ import {type e1, type e2} from "e"
+ import f1, {} from "f"
+ import g1, {g2} from "g"
+ import h1, {type h2} from "h"
+ import * as i1 from "i"
+ import "j"
+ `,
+ "/Users/user/project/src/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "preserveValueImports": true
+ }
+ }
+ `,
+ },
+ format: "esm",
+ outfile: "/Users/user/project/out.js",
+ mode: "convertformat",
+ });
+ itBundled("tsconfig/TsconfigPreserveValueImportsAndImportsNotUsedAsValuesPreserve", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.ts": /* ts */ `
+ import {} from "a"
+ import {b1} from "b"
+ import {c1, type c2} from "c"
+ import {d1, d2, type d3} from "d"
+ import {type e1, type e2} from "e"
+ import f1, {} from "f"
+ import g1, {g2} from "g"
+ import h1, {type h2} from "h"
+ import * as i1 from "i"
+ import "j"
+ `,
+ "/Users/user/project/src/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "importsNotUsedAsValues": "preserve",
+ "preserveValueImports": true
+ }
+ }
+ `,
+ },
+ format: "esm",
+ outfile: "/Users/user/project/out.js",
+ mode: "convertformat",
+ });
+ itBundled("tsconfig/TsconfigTarget", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.ts": /* ts */ `
+ import "./es2018"
+ import "./es2019"
+ import "./es2020"
+ import "./es4"
+ `,
+ "/Users/user/project/src/es2018/index.ts": /* ts */ `
+ let x = { ...y } // es2018 syntax
+ try { y } catch {} // es2019 syntax
+ x?.y() // es2020 syntax
+ `,
+ "/Users/user/project/src/es2019/index.ts": /* ts */ `
+ let x = { ...y } // es2018 syntax
+ try { y } catch {} // es2019 syntax
+ x?.y() // es2020 syntax
+ `,
+ "/Users/user/project/src/es2020/index.ts": /* ts */ `
+ let x = { ...y } // es2018 syntax
+ try { y } catch {} // es2019 syntax
+ x?.y() // es2020 syntax
+ `,
+ "/Users/user/project/src/es4/index.ts": ``,
+ "/Users/user/project/src/es2018/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "target": "ES2018"
+ }
+ }
+ `,
+ "/Users/user/project/src/es2019/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "target": "es2019"
+ }
+ }
+ `,
+ "/Users/user/project/src/es2020/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "target": "ESNext"
+ }
+ }
+ `,
+ "/Users/user/project/src/es4/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "target": "ES4"
+ }
+ }
+ `,
+ },
+ outfile: "/Users/user/project/out.js",
+ /* TODO FIX expectedScanLog: `Users/user/project/src/es4/tsconfig.json: WARNING: Unrecognized target environment "ES4"
+ `, */
+ });
+ itBundled("tsconfig/TsconfigTargetError", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.ts": `x = 123n`,
+ "/Users/user/project/src/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "target": "ES2019"
+ }
+ }
+ `,
+ },
+ outfile: "/Users/user/project/out.js",
+ /* TODO FIX expectedScanLog: `Users/user/project/src/entry.ts: ERROR: Big integer literals are not available in the configured target environment ("ES2019")
+ Users/user/project/src/tsconfig.json: NOTE: The target environment was set to "ES2019" here:
+ `, */
+ });
+ itBundled("tsconfig/TsconfigTargetIgnored", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.ts": `x = 123n`,
+ "/Users/user/project/src/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "target": "ES2019"
+ }
+ }
+ `,
+ },
+ outfile: "/Users/user/project/out.js",
+ });
+ itBundled("tsconfig/TsconfigUseDefineForClassFieldsES2020", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.ts": /* ts */ `
+ Foo = class {
+ useDefine = false
+ }
+ `,
+ "/Users/user/project/src/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "target": "ES2020"
+ }
+ }
+ `,
+ },
+ });
+ itBundled("tsconfig/TsconfigUseDefineForClassFieldsESNext", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.ts": /* ts */ `
+ Foo = class {
+ useDefine = true
+ }
+ `,
+ "/Users/user/project/src/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "target": "ESNext"
+ }
+ }
+ `,
+ },
+ });
+ itBundled("tsconfig/TsconfigUnrecognizedTargetWarning", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.ts": /* ts */ `
+ import "./a"
+ import "b"
+ `,
+ "/Users/user/project/src/a/index.ts": ``,
+ "/Users/user/project/src/a/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "target": "es3"
+ }
+ }
+ `,
+ "/Users/user/project/src/node_modules/b/index.ts": ``,
+ "/Users/user/project/src/node_modules/b/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "target": "es3"
+ }
+ }
+ `,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/src/a/tsconfig.json: WARNING: Unrecognized target environment "es3"
+ `, */
+ });
+ itBundled("tsconfig/TsconfigTargetWarning", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.ts": `await 0`,
+ "/Users/user/project/src/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "target": "es6"
+ }
+ }
+ `,
+ },
+ outfile: "/Users/user/project/out.js",
+ unsupportedJSFeatures: "es6",
+ /* TODO FIX expectedScanLog: `Users/user/project/src/entry.ts: ERROR: Top-level await is not available in the configured target environment ("es6")
+ Users/user/project/src/tsconfig.json: NOTE: The target environment was set to "es6" here:
+ `, */
+ });
+ itBundled("tsconfig/TsconfigOverriddenTargetWarning", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.ts": `await 0`,
+ "/Users/user/project/src/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "target": "es6"
+ }
+ }
+ `,
+ },
+ outfile: "/Users/user/project/out.js",
+ unsupportedJSFeatures: "es2020",
+ targetFromAPI: "TargetWasConfigured",
+ /* TODO FIX expectedScanLog: `Users/user/project/src/entry.ts: ERROR: Top-level await is not available in the configured target environment (es2020)
+ `, */
+ });
+ itBundled("tsconfig/TsConfigNoBaseURLExtendsPaths", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.ts": /* ts */ `
+ import { foo } from "foo"
+ console.log(foo)
+ `,
+ "/Users/user/project/lib/foo.ts": `export let foo = 123`,
+ "/Users/user/project/tsconfig.json": /* json */ `
+ {
+ "extends": "./base/defaults"
+ }
+ `,
+ "/Users/user/project/base/defaults.json": /* json */ `
+ {
+ "compilerOptions": {
+ "paths": {
+ "*": ["lib/*"]
+ }
+ }
+ }
+ `,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/base/defaults.json: WARNING: Non-relative path "lib/*" is not allowed when "baseUrl" is not set (did you forget a leading "./"?)
+ Users/user/project/src/entry.ts: ERROR: Could not resolve "foo"
+ NOTE: You can mark the path "foo" as external to exclude it from the bundle, which will remove this error.
+ `, */
+ });
+ itBundled("tsconfig/TsConfigBaseURLExtendsPaths", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.ts": /* ts */ `
+ import { foo } from "foo"
+ console.log(foo)
+ `,
+ "/Users/user/project/lib/foo.ts": `export let foo = 123`,
+ "/Users/user/project/tsconfig.json": /* json */ `
+ {
+ "extends": "./base/defaults",
+ "compilerOptions": {
+ "baseUrl": "."
+ }
+ }
+ `,
+ "/Users/user/project/base/defaults.json": /* json */ `
+ {
+ "compilerOptions": {
+ "paths": {
+ "*": ["lib/*"]
+ }
+ }
+ }
+ `,
+ },
+ });
+ itBundled("tsconfig/TsConfigPathsExtendsBaseURL", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.ts": /* ts */ `
+ import { foo } from "foo"
+ console.log(foo)
+ `,
+ "/Users/user/project/base/test/lib/foo.ts": `export let foo = 123`,
+ "/Users/user/project/tsconfig.json": /* json */ `
+ {
+ "extends": "./base/defaults",
+ "compilerOptions": {
+ "paths": {
+ "*": ["lib/*"]
+ }
+ }
+ }
+ `,
+ "/Users/user/project/base/defaults.json": /* json */ `
+ {
+ "compilerOptions": {
+ "baseUrl": "test"
+ }
+ }
+ `,
+ },
+ });
+ itBundled("tsconfig/TsConfigModuleSuffixesInsert", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.ts": /* ts */ `
+ import "./foo"
+ import "./bar.js"
+ import "./baz.a.js"
+ `,
+ "/Users/user/project/src/foo.a.ts": `console.log('foo.a')`,
+ "/Users/user/project/src/foo.b.ts": `console.log('foo.b')`,
+ "/Users/user/project/src/foo.ts": `console.log('foo')`,
+ "/Users/user/project/src/bar.a.ts": `console.log('bar.a')`,
+ "/Users/user/project/src/bar.b.ts": `console.log('bar.b')`,
+ "/Users/user/project/src/bar.ts": `console.log('bar')`,
+ "/Users/user/project/src/baz.a.ts": `console.log('baz.a')`,
+ "/Users/user/project/src/baz.b.ts": `console.log('baz.b')`,
+ "/Users/user/project/src/baz.ts": `console.log('baz')`,
+ "/Users/user/project/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "moduleSuffixes": [".a", ".b", ""]
+ }
+ }
+ `,
+ },
+ });
+ itBundled("tsconfig/TsConfigModuleSuffixesNoInsert", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.ts": /* ts */ `
+ import "./foo.b"
+ import "./bar.js"
+ import "./baz.b.js"
+ `,
+ "/Users/user/project/src/foo.a.ts": `console.log('foo.a')`,
+ "/Users/user/project/src/foo.b.ts": `console.log('foo.b')`,
+ "/Users/user/project/src/foo.ts": `console.log('foo')`,
+ "/Users/user/project/src/bar.ts": `console.log('bar')`,
+ "/Users/user/project/src/baz.a.ts": `console.log('baz.a')`,
+ "/Users/user/project/src/baz.b.ts": `console.log('baz.b')`,
+ "/Users/user/project/src/baz.ts": `console.log('baz')`,
+ "/Users/user/project/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "moduleSuffixes": [".a", ".b", ""]
+ }
+ }
+ `,
+ },
+ });
+ itBundled("tsconfig/TsConfigModuleSuffixesNoEmpty", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.ts": /* ts */ `
+ import "./foo.js"
+ import "./bar"
+ `,
+ "/Users/user/project/src/foo.b.ts": `console.log('foo.b')`,
+ "/Users/user/project/src/bar.ts": `console.log('bar')`,
+ "/Users/user/project/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "moduleSuffixes": [".a", ".b"]
+ }
+ }
+ `,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/src/entry.ts: ERROR: Could not resolve "./bar"
+ `, */
+ });
+ itBundled("tsconfig/TsConfigWithStatementAlwaysStrictFalse", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.ts": `with (x) y`,
+ "/Users/user/project/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "alwaysStrict": false
+ }
+ }
+ `,
+ },
+ outfile: "/Users/user/project/out.js",
+ });
+ itBundled("tsconfig/TsConfigWithStatementAlwaysStrictTrue", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.ts": `with (x) y`,
+ "/Users/user/project/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "alwaysStrict": true
+ }
+ }
+ `,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/src/entry.ts: ERROR: With statements cannot be used in strict mode
+ Users/user/project/tsconfig.json: NOTE: TypeScript's "alwaysStrict" setting was enabled here:
+ `, */
+ });
+ itBundled("tsconfig/TsConfigWithStatementStrictFalse", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.ts": `with (x) y`,
+ "/Users/user/project/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "strict": false
+ }
+ }
+ `,
+ },
+ outfile: "/Users/user/project/out.js",
+ });
+ itBundled("tsconfig/TsConfigWithStatementStrictTrue", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.ts": `with (x) y`,
+ "/Users/user/project/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "strict": true
+ }
+ }
+ `,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/src/entry.ts: ERROR: With statements cannot be used in strict mode
+ Users/user/project/tsconfig.json: NOTE: TypeScript's "strict" setting was enabled here:
+ `, */
+ });
+ itBundled("tsconfig/TsConfigWithStatementStrictFalseAlwaysStrictTrue", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.ts": `with (x) y`,
+ "/Users/user/project/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "strict": false,
+ "alwaysStrict": true
+ }
+ }
+ `,
+ },
+ /* TODO FIX expectedScanLog: `Users/user/project/src/entry.ts: ERROR: With statements cannot be used in strict mode
+ Users/user/project/tsconfig.json: NOTE: TypeScript's "alwaysStrict" setting was enabled here:
+ `, */
+ });
+ itBundled("tsconfig/TsConfigWithStatementStrictTrueAlwaysStrictFalse", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/entry.ts": `with (x) y`,
+ "/Users/user/project/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "strict": true,
+ "alwaysStrict": false
+ }
+ }
+ `,
+ },
+ outfile: "/Users/user/project/out.js",
+ });
+ itBundled("tsconfig/TsConfigAlwaysStrictTrueEmitDirectivePassThrough", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/implicit.ts": `console.log('this file should start with "use strict"')`,
+ "/Users/user/project/src/explicit.ts": /* ts */ `
+ 'use strict'
+ console.log('this file should start with "use strict"')
+ `,
+ "/Users/user/project/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "alwaysStrict": true
+ }
+ }
+ `,
+ },
+ entryPoints: ["/Users/user/project/src/implicit.ts", "/Users/user/project/src/explicit.ts"],
+ mode: "passthrough",
+ });
+ itBundled("tsconfig/TsConfigAlwaysStrictTrueEmitDirectiveFormat", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/implicit.ts": `console.log('this file should start with "use strict"')`,
+ "/Users/user/project/src/explicit.ts": /* ts */ `
+ 'use strict'
+ console.log('this file should start with "use strict"')
+ `,
+ "/Users/user/project/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "alwaysStrict": true
+ }
+ }
+ `,
+ },
+ entryPoints: ["/Users/user/project/src/implicit.ts", "/Users/user/project/src/explicit.ts"],
+ mode: "convertformat",
+ });
+ itBundled("tsconfig/TsConfigAlwaysStrictTrueEmitDirectiveBundleIIFE", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/implicit.ts": `console.log('this file should start with "use strict"')`,
+ "/Users/user/project/src/explicit.ts": /* ts */ `
+ 'use strict'
+ console.log('this file should start with "use strict"')
+ `,
+ "/Users/user/project/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "alwaysStrict": true
+ }
+ }
+ `,
+ },
+ entryPoints: ["/Users/user/project/src/implicit.ts", "/Users/user/project/src/explicit.ts"],
+ outdir: "/Users/user/project/out",
+ });
+ itBundled("tsconfig/TsConfigAlwaysStrictTrueEmitDirectiveBundleCJS", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/implicit.ts": `console.log('this file should start with "use strict"')`,
+ "/Users/user/project/src/explicit.ts": /* ts */ `
+ 'use strict'
+ console.log('this file should start with "use strict"')
+ `,
+ "/Users/user/project/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "alwaysStrict": true
+ }
+ }
+ `,
+ },
+ entryPoints: ["/Users/user/project/src/implicit.ts", "/Users/user/project/src/explicit.ts"],
+ outdir: "/Users/user/project/out",
+ });
+ itBundled("tsconfig/TsConfigAlwaysStrictTrueEmitDirectiveBundleESM", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/implicit.ts": `console.log('this file should not start with "use strict"')`,
+ "/Users/user/project/src/explicit.ts": /* ts */ `
+ 'use strict'
+ console.log('this file should not start with "use strict"')
+ `,
+ "/Users/user/project/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "alwaysStrict": true
+ }
+ }
+ `,
+ },
+ entryPoints: ["/Users/user/project/src/implicit.ts", "/Users/user/project/src/explicit.ts"],
+ outdir: "/Users/user/project/out",
+ });
+ itBundled("tsconfig/TsConfigExtendsDotWithoutSlash", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/main.ts": `console.log(123n)`,
+ "/Users/user/project/src/foo.json": /* json */ `
+ {
+ "extends": "."
+ }
+ `,
+ "/Users/user/project/src/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "target": "ES6"
+ }
+ }
+ `,
+ },
+ outdir: "/Users/user/project/out",
+ format: "esm",
+ /* TODO FIX expectedScanLog: `Users/user/project/src/main.ts: ERROR: Big integer literals are not available in the configured target environment ("ES6")
+ Users/user/project/src/tsconfig.json: NOTE: The target environment was set to "ES6" here:
+ `, */
+ });
+ itBundled("tsconfig/TsConfigExtendsDotDotWithoutSlash", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/main.ts": `console.log(123n)`,
+ "/Users/user/project/src/tsconfig.json": /* json */ `
+ {
+ "extends": ".."
+ }
+ `,
+ "/Users/user/project/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "target": "ES6"
+ }
+ }
+ `,
+ },
+ outdir: "/Users/user/project/out",
+ /* TODO FIX expectedScanLog: `Users/user/project/src/main.ts: ERROR: Big integer literals are not available in the configured target environment ("ES6")
+ Users/user/project/tsconfig.json: NOTE: The target environment was set to "ES6" here:
+ `, */
+ });
+ itBundled("tsconfig/TsConfigExtendsDotWithSlash", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/main.ts": `console.log(123n)`,
+ "/Users/user/project/src/foo.json": /* json */ `
+ {
+ "extends": "./"
+ }
+ `,
+ "/Users/user/project/src/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "target": "ES6"
+ }
+ }
+ `,
+ },
+ outdir: "/Users/user/project/out",
+ format: "esm",
+ /* TODO FIX expectedScanLog: `Users/user/project/src/foo.json: WARNING: Cannot find base config file "./"
+ `, */
+ });
+ itBundled("tsconfig/TsConfigExtendsDotDotWithSlash", {
+ // GENERATED
+ files: {
+ "/Users/user/project/src/main.ts": `console.log(123n)`,
+ "/Users/user/project/src/tsconfig.json": /* json */ `
+ {
+ "extends": "../"
+ }
+ `,
+ "/Users/user/project/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "target": "ES6"
+ }
+ }
+ `,
+ },
+ outdir: "/Users/user/project/out",
+ /* TODO FIX expectedScanLog: `Users/user/project/src/tsconfig.json: WARNING: Cannot find base config file "../"
+ `, */
+ });
+});
diff --git a/test/bundler/expectBundled.md b/test/bundler/expectBundled.md
new file mode 100644
index 000000000..f8883459d
--- /dev/null
+++ b/test/bundler/expectBundled.md
@@ -0,0 +1,184 @@
+# `bun build` tests using `expectBundled`
+
+Most bundler tests were ported [from esbuild][1], located in `test/bundler/esbuild`. Our own tests are in `bundler_*.test.ts`
+
+[1]: https://github.com/evanw/esbuild/tree/main/internal/bundler_tests
+
+Call `expectBundled` within a test to test the bundler. The `id` passed as the first argument must be unique across the all tests, and generally uses the format `file/TestName`. The second parameter is an options object.
+
+All bundle entry files, their outputs, and other helpful files are written to disk at: `$TEMP/bun-build-tests/{run_id}/{id}`. This can be used to inspect and debug bundles, as they are not deleted after runtime.
+
+In addition to comparing the bundle outputs against snapshots, **most test cases execute the bundle and have additional checks to assert the intended logic is happening properly**. This allows the bundler to change exactly how it writes files (optimizations / variable renaming), and still have concrete tests that ensure what the bundler creates will function properly. Snapshots are also taken, but these are used to check for regressions and not neccessarily check accuracy.
+
+On top of `expectBundled`, there is also `itBundled` which wraps `expectBundled` and `it` together, which is what we mostly use in our tests.
+
+These two functions have many options you can pass to it, check the examples below for some common use cases, then look at the `BundlerTestInput` for a complete set of options. Not all of the options are implemented; these tests get auto-skipped.
+
+## Running tests
+
+You can use `bun test` as normal, but `expectBundled` looks for these environment variables:
+
+- `BUN_BUNDLER_TEST_USE_ESBUILD` - Use `esbuild` instead of `bun build`.
+- `BUN_BUNDLER_TEST_DEBUG` - Write extra files to disk and log extra info.
+- `BUN_BUNDLER_TEST_FILTER` - Set this to the exact id of a test to only run that test.
+- `BUN_EXE` - Override the path to the `bun` executable.
+
+There is also a helper CLI that sets these variables:
+
+```sh
+$ ./run-single-bundler-test.sh default/ExportMissingES6
+$ ./run-single-bundler-test.sh default/ExportMissingES6 e
+```
+
+Passing the second argument at all will use `esbuild` instead of `bun build`. It also creates a symlink `./out` to the output directory, for faster inspection. I have this aliased to `tb` (test bun) in my shell for fast usage.
+
+## Basic Examples and Common Patterns
+
+At the start of test files, use `testForFile` instead of importing from `bun:test`:
+
+```ts
+import { bundlerTest, expectBundled, itBundled, testForFile } from "./expectBundled";
+var { describe, test, expect } = testForFile(import.meta.path);
+```
+
+Basic example (this goes in a `describe`)
+
+```ts
+itBundled("default/SimpleES6", {
+ files: {
+ // Define one or more files. Strings are passed through `dedent`
+ // First file is the entry file
+ "/entry.js": /* js */ `
+ import { fn } from './foo';
+ console.log(fn());
+ `,
+ "/foo.js": /* js */ `
+ export function fn() {
+ return 123
+ }
+ `,
+ },
+ // outfile: "out.js", // Default is "out.js"
+
+ // Passing `run` will run the bundle
+ run: {
+ stdout: "123",
+ // file: "out.js", // Default is whatever `outfile` is
+ },
+});
+```
+
+Testing the exports of a module using `runtimeFiles`:
+
+```ts
+itBundled("importstar/ExportSelfES6", {
+ files: {
+ "/entry.js": /* js */ `
+ export const foo = 123
+ export * from './entry'
+ `,
+ },
+ format: "esm",
+ // `runtimeFiles` are only available after the bundle is created, which lets you
+ // keep some files secret, like preventing externals from being bundled, etc.
+ // It can also be used to provide a runtime entry point.
+ runtimeFiles: {
+ "/test.js": /* js */ `
+ import * as foo from './out.js'
+ // Try avoiding relying on Bun's object formatter, instead use JSON.stringify when possible
+ // This will avoid any changes to how these objects are formatted.
+ console.log(JSON.stringify(foo));
+ `,
+ },
+ run: {
+ file: "/test.js",
+ // console.log is a great way to assert the proper values exist, but when needed you
+ // can also reach for `import "assert"` and run that in the test.
+ stdout: '{"foo":123}',
+ },
+});
+```
+
+You can use a `test.js` to define extra variables via `globalThis`:
+
+```ts
+itBundled("default/MinifiedBundleEndingWithImportantSemicolon", {
+ files: {
+ // foo() is not defined in this scope
+ "/entry.js": `while(foo()); // This semicolon must not be stripped`,
+
+ "/test.js": /* js */ `
+ let i = 0;
+ // let's define foo()
+ globalThis.foo = () => {
+ console.log(i++);
+ return i === 1;
+ };
+ await import('./out.js')
+ `,
+ },
+ minifyWhitespace: true,
+ format: "iife",
+ run: {
+ file: "/test.js",
+ stdout: "0\n1",
+ },
+});
+```
+
+## onAfterBundle
+
+Since not every possible test case can be covered by `run` and the other options, you can pass a function `onAfterBundle` to add custom checks.
+
+```ts
+itBundled("default/ThisOutsideFunctionRenamedToExports", {
+ files: {
+ "/entry.js": /* js */ `
+ console.log(this)
+ console.log((x = this) => this)
+ console.log({x: this})
+ console.log(class extends this.foo {})
+ console.log(class { [this.foo] })
+ console.log(class { [this.foo]() {} })
+ console.log(class { static [this.foo] })
+ console.log(class { static [this.foo]() {} })
+ `,
+ },
+ onAfterBundle(api) {
+ if (api.readFile("/out.js").includes("this")) {
+ throw new Error("All cases of `this` should have been rewritten to `exports`");
+ }
+ },
+});
+```
+
+Check the `BundlerTestBundleAPI` typedef for all available methods. Note that `api.readFile` is cached so you can call it multiple times without worrying about anything.
+
+This callback is run before `run`, so you can use tricks like `appendFile` to add extra data, useful when testing IIFE bundles in combination with `globalName`
+
+```ts
+itBundled("importstar/ReExportStarExternalIIFE", {
+ files: {
+ "/entry.js": `export * from "foo"`,
+ },
+ format: "iife",
+ globalName: "mod",
+ external: ["foo"],
+ runtimeFiles: {
+ "/node_modules/foo/index.js": /* js */ `
+ export const foo = 'foo'
+ export const bar = 'bar'
+ `,
+ },
+ onAfterBundle(api) {
+ api.appendFile("/out.js", "\nconsole.log(JSON.stringify(mod))");
+ },
+ run: {
+ stdout: '{"bar":"bar","foo":"foo"}',
+ },
+});
+```
+
+## 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.
diff --git a/test/bundler/expectBundled.ts b/test/bundler/expectBundled.ts
new file mode 100644
index 000000000..87e31b447
--- /dev/null
+++ b/test/bundler/expectBundled.ts
@@ -0,0 +1,760 @@
+/**
+ * See `./expectBundled.md` for how this works.
+ */
+import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "fs";
+import path from "path";
+import dedent from "dedent";
+import { bunEnv, bunExe } from "harness";
+import { tmpdir } from "os";
+import { callerSourceOrigin } from "bun:jsc";
+import { fileURLToPath } from "bun";
+import type { Expect } from "bun:test";
+
+type BunTestExports = typeof import("bun:test");
+export function testForFile(file: string): BunTestExports {
+ if (file.startsWith("file://")) {
+ file = fileURLToPath(new URL(file));
+ }
+
+ var testFile = testFiles.get(file);
+ if (!testFile) {
+ testFile = (Bun as any).jest(file);
+ testFiles.set(file, testFile);
+ }
+ return testFile;
+}
+
+/** Use `esbuild` instead of `bun build` */
+const ESBUILD = process.env.BUN_BUNDLER_TEST_USE_ESBUILD;
+/** Write extra files to disk and log extra info. */
+const DEBUG = process.env.BUN_BUNDLER_TEST_DEBUG;
+/** Set this to the id of a bundle test to run just that test */
+const FILTER = process.env.BUN_BUNDLER_TEST_FILTER;
+/** Path to the bun. TODO: Once bundler is merged, we should remove the `bun-debug` fallback. */
+const BUN_EXE = (process.env.BUN_EXE && Bun.which(process.env.BUN_EXE)) ?? Bun.which("bun-debug") ?? bunExe();
+
+const outBaseTemplate = path.join(tmpdir(), "bun-build-tests", `${ESBUILD ? "esbuild" : "bun"}-`);
+if (!existsSync(path.dirname(outBaseTemplate))) mkdirSync(path.dirname(outBaseTemplate), { recursive: true });
+const outBase = mkdtempSync(outBaseTemplate);
+const testsRan = new Set();
+
+if (ESBUILD) {
+ console.warn("NOTE: using esbuild for bun build tests");
+}
+
+export interface BundlerTestInput {
+ // file options
+ files: Record<string, string>;
+ /** Files to be written only after the bundle is done. */
+ runtimeFiles?: Record<string, string>;
+ /** Defaults to the first item in `files` */
+ entryPoints?: string[];
+ /** ??? */
+ entryPointsAdvanced?: Array<{ input: string; output?: string }>;
+ /** These are not path resolved. Used for `default/RelativeEntryPointError` */
+ entryPointsRaw?: string[];
+ /** Defaults to bundle */
+ mode?: "bundle" | "transform";
+ /** Used for `default/ErrorMessageCrashStdinIssue2913`. */
+ stdin?: { contents: string; cwd: string };
+ /** Use when doing something weird with entryPoints and you need to check other output paths. */
+ outputPaths?: string[];
+
+ // bundler options
+ alias?: Record<string, string>;
+ assetNames?: string;
+ banner?: string;
+ define?: Record<string, string | number>;
+ /** Default is "[name].[ext]" */
+ entryNames?: string;
+ extensionOrder?: string[];
+ /** Replaces "{{root}}" with the file root */
+ external?: string[];
+ /** Defaults to "esm" */
+ format?: "esm" | "cjs" | "iife";
+ globalName?: string;
+ ignoreDCEAnnotations?: boolean;
+ inject?: string[];
+ jsx?: {
+ factory?: string;
+ fragment?: string;
+ automaticRuntime?: boolean;
+ development?: boolean;
+ };
+ outbase?: string;
+ /** Defaults to `/out.js` */
+ outfile?: string;
+ /** Defaults to `/out` */
+ outdir?: string;
+ /** Defaults to "browser". "bun" is set to "node" when using esbuild. */
+ platform?: "bun" | "node" | "neutral" | "browser";
+ publicPath?: string;
+ keepNames?: boolean;
+ legalComments?: "none" | "inline" | "eof" | "linked" | "external";
+ loader?: Record<string, string>;
+ mangleProps?: RegExp;
+ mangleQuoted?: boolean;
+ mainFields?: string[];
+ metafile?: boolean | string;
+ minifyIdentifiers?: boolean;
+ minifySyntax?: boolean;
+ targetFromAPI?: "TargetWasConfigured";
+ minifyWhitespace?: boolean;
+ splitting?: boolean;
+ treeShaking?: boolean;
+ unsupportedCSSFeatures?: string[];
+ unsupportedJSFeatures?: string[];
+ useDefineForClassFields?: boolean;
+ sourceMap?: boolean | "inline" | "external";
+
+ // assertion options
+
+ /**
+ * If passed, the bundle should fail with given error messages.
+ *
+ * Pass an object mapping filenames to an array of error strings that file should contain.
+ */
+ bundleErrors?: true | Record<string, string[]>;
+ /**
+ * Same as bundleErrors except for warnings. Bundle should still succeed.
+ */
+ bundleWarnings?: true | Record<string, string[]>;
+ /**
+ * Setting to true or an object will cause the file to be run with bun.
+ * Pass an array to run multiple times with different options.
+ */
+ run?: boolean | BundlerTestRunOptions | BundlerTestRunOptions[];
+
+ /**
+ * Shorthand for testing dead code elimination cases.
+ * Checks source code for REMOVE, FAIL, DROP, which will fail the test.
+ */
+ dce?: boolean;
+ /**
+ * 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.
+ */
+ assertNotPresent?: Record<string, string | string[]>;
+
+ /** Used on tests in the esbuild suite that fail and skip. */
+ skipOnEsbuild?: boolean;
+
+ /** Run after bundle happens but before runtime. */
+ onAfterBundle?(api: BundlerTestBundleAPI): void;
+}
+
+export interface BundlerTestBundleAPI {
+ root: string;
+ outfile: string;
+ outdir: string;
+
+ readFile(file: string): string;
+ writeFile(file: string, contents: string): void;
+ prependFile(file: string, contents: string): void;
+ appendFile(file: string, contents: string): void;
+ expectFile(file: string): Expect;
+ assertFileExists(file: string): void;
+
+ warnings: Record<string, { file: string; error: string; line?: string; col?: string }[]>;
+ options: BundlerTestInput;
+}
+
+export interface BundlerTestRunOptions {
+ /** Override file to run, instead of `options.absOutputFile` */
+ file?: string;
+ /** Pass args to the program */
+ args?: string[];
+ /** Pass args to bun itself (before the filename) */
+ bunArgs?: string[];
+ /** match exact stdout */
+ stdout?: string;
+ /** match exact error message, example "ReferenceError: Can't find variable: bar" */
+ error?: string;
+ /**
+ * for extra confidence the error is correctly tested for, a regex for the line it was
+ * thrown on can be passed. this should be replaced with a source map lookup when that's
+ * available to us.
+ */
+ errorLineMatch?: RegExp;
+
+ runtime?: "bun" | "node";
+}
+
+var testFiles = new Map();
+
+export function expectBundled(id: string, opts: BundlerTestInput, dryRun?: boolean) {
+ var { expect, it, test } = testForFile(callerSourceOrigin());
+ if (FILTER && id !== FILTER) return;
+
+ let {
+ assertNotPresent,
+ banner,
+ bundleErrors,
+ bundleWarnings,
+ dce,
+ define,
+ entryNames,
+ entryPoints,
+ entryPointsRaw,
+ external,
+ files,
+ format,
+ globalName,
+ inject,
+ jsx = {},
+ legalComments,
+ metafile,
+ minifyIdentifiers,
+ minifySyntax,
+ minifyWhitespace,
+ mode,
+ onAfterBundle,
+ outdir,
+ outfile,
+ outputPaths,
+ platform,
+ run,
+ runtimeFiles,
+ skipOnEsbuild,
+ sourceMap,
+ splitting,
+ unsupportedCSSFeatures,
+ unsupportedJSFeatures,
+ ...unknownProps
+ } = opts;
+
+ // TODO: Remove this check once all options have been implemented
+ if (Object.keys(unknownProps).length > 0) {
+ throw new Error("expectBundled recieved unexpected options: " + Object.keys(unknownProps).join(", "));
+ }
+
+ // This is a sanity check that protects against bad copy pasting.
+ if (testsRan.has(id)) {
+ throw new Error(`expectBundled("${id}", ...) was called twice. Check your tests for bad copy+pasting.`);
+ }
+
+ // Resolve defaults for options and some related things
+ mode ??= "bundle";
+ platform ??= "browser";
+ format ??= "esm";
+ entryPoints ??= entryPointsRaw ? [] : [Object.keys(files)[0]];
+ if (run === true) run = {};
+ if (metafile === true) metafile = "/metafile.json";
+ if (bundleErrors === true) bundleErrors = {};
+ 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");
+ }
+ if (!ESBUILD && metafile) {
+ throw new Error("metafile not implemented in bun build");
+ }
+ if (!ESBUILD && legalComments) {
+ throw new Error("legalComments not implemented in bun build");
+ }
+ if (!ESBUILD && unsupportedJSFeatures && unsupportedJSFeatures.length) {
+ throw new Error("unsupportedJSFeatures not implemented in bun build");
+ }
+ if (!ESBUILD && unsupportedCSSFeatures && unsupportedCSSFeatures.length) {
+ throw new Error("unsupportedCSSFeatures not implemented in bun build");
+ }
+ if (ESBUILD && skipOnEsbuild) {
+ return;
+ }
+ if (dryRun) {
+ return;
+ }
+
+ const root = path.join(outBase, id.replaceAll("/", path.sep));
+ if (DEBUG) console.log("root:", root);
+
+ const entryPaths = entryPoints.map(file => path.join(root, file));
+
+ if (external) {
+ external = external.map(x => x.replace(/\{\{root\}\}/g, root));
+ }
+
+ 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)));
+
+ if (outdir) {
+ entryNames ??= "[name].[ext]";
+ }
+
+ // Option validation
+ if (entryPaths.length !== 1 && outfile && !entryPointsRaw) {
+ throw new Error("Test cannot specify `outfile` when more than one entry path.");
+ }
+
+ // Prepare source folder
+ if (existsSync(root)) {
+ rmSync(root, { recursive: true });
+ }
+ for (const [file, contents] of Object.entries(files)) {
+ const filename = path.join(root, file);
+ mkdirSync(path.dirname(filename), { recursive: true });
+ writeFileSync(filename, dedent(contents).replace(/\{\{root\}\}/g, root));
+ }
+
+ // Run bun build cli. In the future we can move to using `Bun.Bundler`
+ const cmd = (
+ !ESBUILD
+ ? [
+ ...(process.env.BUN_DEBUGGER ? ["lldb-server", "g:1234", "--"] : []),
+ BUN_EXE,
+ "build",
+ ...entryPaths,
+ ...(entryPointsRaw ?? []),
+ outfile ? `--outfile=${outfile}` : `--outdir=${outdir}`,
+ define && Object.entries(define).map(([k, v]) => ["--define", `${k}=${v}`]),
+ `--platform=${platform}`,
+ 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",
+ jsx.factory && `--jsx-factory=${jsx.factory}`,
+ jsx.fragment && `--jsx-fragment=${jsx.fragment}`,
+ jsx.development && `--jsx-dev`,
+ // metafile && `--metafile=${metafile}`,
+ // sourceMap && `--sourcemap${sourceMap !== true ? `=${sourceMap}` : ""}`,
+ entryNames && entryNames !== "[name].[ext]" && [`--entry-names`, entryNames],
+ // `--format=${format}`,
+ // legalComments && `--legal-comments=${legalComments}`,
+ splitting && `--splitting`,
+ ]
+ : [
+ Bun.which("esbuild"),
+ mode === "bundle" && "--bundle",
+ outfile ? `--outfile=${outfile}` : `--outdir=${outdir}`,
+ `--format=${format}`,
+ `--platform=${platform === "bun" ? "node" : platform}`,
+ 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)}`),
+ define && Object.entries(define).map(([k, v]) => `--define:${k}=${v}`),
+ jsx.automaticRuntime && "--jsx=automatic",
+ jsx.factory && `--jsx-factory=${jsx.factory}`,
+ jsx.fragment && `--jsx-fragment=${jsx.fragment}`,
+ jsx.development && `--jsx-dev`,
+ entryNames && entryNames !== "[name].[ext]" && `--entry-names=${entryNames.replace(/\.\[ext]$/, "")}`,
+ metafile && `--metafile=${metafile}`,
+ sourceMap && `--sourcemap${sourceMap !== true ? `=${sourceMap}` : ""}`,
+ banner && `--banner:js=${banner}`,
+ legalComments && `--legal-comments=${legalComments}`,
+ splitting && `--splitting`,
+ [...(unsupportedJSFeatures ?? []), ...(unsupportedCSSFeatures ?? [])].map(x => `--supported:${x}=false`),
+ ...entryPaths,
+ ...(entryPointsRaw ?? []),
+ ]
+ )
+ .flat(Infinity)
+ .filter(Boolean)
+ .map(x => String(x)) as [string, ...string[]];
+
+ if (DEBUG) {
+ writeFileSync(
+ path.join(root, "run.sh"),
+ "#!/bin/sh\n" + cmd.map(x => (x.match(/^[a-z0-9_:=\./\\-]+$/i) ? x : `"${x.replace(/"/g, '\\"')}"`)).join(" "),
+ );
+ try {
+ mkdirSync(path.join(root, ".vscode"), { recursive: true });
+ } catch (e) {}
+
+ writeFileSync(
+ path.join(root, ".vscode", "launch.json"),
+ JSON.stringify(
+ {
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "lldb",
+ "request": "launch",
+ "name": "bun test",
+ "program": cmd[0],
+ "args": cmd.slice(1),
+ "cwd": root,
+ "env": {
+ "FORCE_COLOR": "1",
+ },
+ "console": "internalConsole",
+ },
+ ],
+ },
+ null,
+ 2,
+ ),
+ );
+ }
+
+ const { stdout, stderr, success } = Bun.spawnSync({
+ cmd,
+ cwd: root,
+ stdio: ["ignore", "pipe", "pipe"],
+ env: bunEnv,
+ });
+
+ // Check for errors
+ const expectedErrors = bundleErrors
+ ? Object.entries(bundleErrors).flatMap(([file, v]) => v.map(error => ({ file, error })))
+ : null;
+
+ if (!success) {
+ if (!ESBUILD) {
+ const errorRegex = /^error: (.*?)\n(?:.*?\n\s*\^\s*\n(.*?)\n)?/gms;
+ const allErrors = [...stderr!.toString("utf-8").matchAll(errorRegex)].map(([_str1, error, source]) => {
+ if (!source) {
+ return { error, file: "<bun>" };
+ }
+ const [_str2, fullFilename, line, col] = source.match(/bun-build-tests\/(.*):(\d+):(\d+)/)!;
+ const file = fullFilename.slice(id.length + path.basename(outBase).length + 1);
+ return { error, file, line, col };
+ });
+
+ if (allErrors.length === 0) {
+ console.log(stderr!.toString("utf-8"));
+ }
+
+ if (stderr!.toString("utf-8").includes("Crash report saved to:")) {
+ throw new Error("Bun crashed during build");
+ }
+
+ if (DEBUG && allErrors.length) {
+ console.log("REFERENCE ERRORS OBJECT");
+ console.log("bundleErrors: {");
+ const files: any = {};
+ for (const err of allErrors) {
+ files[err.file] ??= [];
+ files[err.file].push(err);
+ }
+ for (const [file, errs] of Object.entries(files)) {
+ console.log(' "' + file + '": [');
+ for (const err of errs as any) {
+ console.log(" `" + err.error + "`");
+ }
+ console.log(" ],");
+ }
+ console.log("},");
+ }
+
+ if (expectedErrors) {
+ const errorsLeft = [...expectedErrors];
+ let unexpectedErrors = [];
+
+ for (const error of allErrors) {
+ const i = errorsLeft.findIndex(item => error.file === item.file && item.error === error.error);
+ if (i === -1) {
+ unexpectedErrors.push(error);
+ } else {
+ errorsLeft.splice(i, 1);
+ }
+ }
+
+ if (unexpectedErrors.length) {
+ throw new Error(
+ "Unexpected errors reported while bundling:\n" +
+ [...unexpectedErrors].map(formatError).join("\n") +
+ "\n\nExpected errors:\n" +
+ expectedErrors.map(formatError).join("\n"),
+ );
+ }
+
+ if (errorsLeft.length) {
+ throw new Error("Errors were expected while bundling:\n" + errorsLeft.map(formatError).join("\n"));
+ }
+
+ return;
+ }
+ throw new Error("Bundle Failed\n" + [...allErrors].map(formatError).join("\n"));
+ } else if (!expectedErrors) {
+ throw new Error("Bundle Failed\n" + stderr?.toString("utf-8"));
+ }
+ return;
+ } else if (expectedErrors) {
+ throw new Error("Errors were expected while bundling:\n" + expectedErrors.map(formatError).join("\n"));
+ }
+
+ // Check for warnings
+ let warningReference: Record<string, { file: string; error: string; line?: string; col?: string }[]> = {};
+ if (!ESBUILD) {
+ const warningRegex = /^warn: (.*?)\n.*?\n\s*\^\s*\n(.*?)\n/gms;
+ const allWarnings = [...stderr!.toString("utf-8").matchAll(warningRegex)].map(([_str1, error, source]) => {
+ const [_str2, fullFilename, line, col] = source.match(/bun-build-tests\/(.*):(\d+):(\d+)/)!;
+ const file = fullFilename.slice(id.length + path.basename(outBase).length + 1);
+ return { error, file, line, col };
+ });
+ const expectedWarnings = bundleWarnings
+ ? Object.entries(bundleWarnings).flatMap(([file, v]) => v.map(error => ({ file, error })))
+ : null;
+
+ for (const err of allWarnings) {
+ warningReference[err.file] ??= [];
+ warningReference[err.file].push(err);
+ }
+ if (DEBUG && allWarnings.length) {
+ console.log("REFERENCE WARNINGS OBJECT");
+ console.log("bundleWarnings: {");
+ for (const [file, errs] of Object.entries(warningReference)) {
+ console.log(' "' + file + '": [');
+ for (const err of errs as any) {
+ console.log(" `" + err.error + "`");
+ }
+ console.log(" ],");
+ }
+ console.log("},");
+ }
+
+ if (allWarnings.length > 0 && !expectedWarnings) {
+ throw new Error("Warnings were thrown while bundling:\n" + allWarnings.map(formatError).join("\n"));
+ } else if (expectedWarnings) {
+ const warningsLeft = [...expectedWarnings];
+ let unexpectedWarnings = [];
+
+ for (const error of allWarnings) {
+ const i = warningsLeft.findIndex(item => error.file === item.file && item.error === error.error);
+ if (i === -1) {
+ unexpectedWarnings.push(error);
+ } else {
+ warningsLeft.splice(i, 1);
+ }
+ }
+
+ if (unexpectedWarnings.length) {
+ throw new Error(
+ "Unexpected warnings reported while bundling:\n" +
+ [...unexpectedWarnings].map(formatError).join("\n") +
+ "\n\nExpected warnings:\n" +
+ expectedWarnings.map(formatError).join("\n"),
+ );
+ }
+
+ if (warningsLeft.length) {
+ throw new Error("Warnings were expected while bundling:\n" + warningsLeft.map(formatError).join("\n"));
+ }
+ }
+ }
+
+ const readCache: Record<string, string> = {};
+ const readFile = (file: string) =>
+ readCache[file] || (readCache[file] = readFileSync(path.join(root, file), "utf-8"));
+ const writeFile = (file: string, contents: string) => {
+ readCache[file] = contents;
+ writeFileSync(path.join(root, file), contents);
+ };
+ const api = {
+ root,
+ outfile: outfile!,
+ outdir: outdir!,
+ readFile,
+ writeFile,
+ expectFile: file => expect(readFile(file)),
+ prependFile: (file, contents) => writeFile(file, dedent(contents) + "\n" + readFile(file)),
+ appendFile: (file, contents) => writeFile(file, readFile(file) + "\n" + dedent(contents)),
+ assertFileExists: file => {
+ if (!existsSync(path.join(root, file))) {
+ throw new Error("Expected file to be written: " + file);
+ }
+ },
+ warnings: warningReference,
+ options: opts,
+ } satisfies BundlerTestBundleAPI;
+
+ // Check that the bundle failed with status code 0 by verifying all files exist.
+ 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)];
+ if (dceFails.length) {
+ throw new Error("DCE test did not remove all expected code in " + outfile + ".");
+ }
+ }
+ if (!ESBUILD) {
+ // expect(readFileSync(outfile).toString()).toMatchSnapshot(outfile.slice(root.length));
+ }
+ }
+ } else {
+ // entryNames makes it so we cannot predict the output file
+ if (!entryNames) {
+ 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) {
+ // TODO: snapshot these test cases
+ }
+ }
+
+ if (assertNotPresent) {
+ for (const [key, value] of Object.entries(assertNotPresent)) {
+ const filepath = path.join(root, key);
+ if (existsSync(filepath)) {
+ const strings = Array.isArray(value) ? value : [value];
+ for (const str of strings) {
+ if (api.readFile(key).includes(str)) throw new Error(`Expected ${key} to not contain "${str}"`);
+ }
+ }
+ }
+ }
+
+ // Write runtime files to disk as well as run the post bundle hook.
+ for (const [file, contents] of Object.entries(runtimeFiles ?? {})) {
+ mkdirSync(path.dirname(path.join(root, file)), { recursive: true });
+ writeFileSync(path.join(root, file), dedent(contents).replace(/\{\{root\}\}/g, root));
+ }
+
+ if (onAfterBundle) {
+ onAfterBundle(api);
+ }
+
+ // Runtime checks!
+ if (run) {
+ const runs = Array.isArray(run) ? run : [run];
+ let i = 0;
+ for (const run of runs) {
+ let prefix = runs.length === 1 ? "" : `[run ${i++}] `;
+
+ let file = run.file;
+ if (file) {
+ file = path.join(root, file);
+ } else if (entryPaths.length === 1) {
+ file = outfile;
+ } else {
+ throw new Error(prefix + "run.file is required when there is more than one entrypoint.");
+ }
+
+ const { success, stdout, stderr } = Bun.spawnSync({
+ cmd: [
+ (run.runtime ?? "bun") === "bun" ? bunExe() : "node",
+ ...(run.bunArgs ?? []),
+ file,
+ ...(run.args ?? []),
+ ] as [string, ...string[]],
+ env: {
+ ...bunEnv,
+ FORCE_COLOR: "0",
+ },
+ stdio: ["ignore", "pipe", "pipe"],
+ });
+
+ if (run.error) {
+ if (success) {
+ throw new Error(
+ prefix +
+ "Bundle should have thrown at runtime\n" +
+ stdout!.toString("utf-8") +
+ "\n" +
+ stderr!.toString("utf-8"),
+ );
+ }
+
+ if (run.errorLineMatch) {
+ // in order to properly analyze the error, we have to look backwards on stderr. this approach
+ // most definetly can be improved but it works fine here.
+ const stack = [];
+ let error;
+ const lines = stderr!
+ .toString("utf-8")
+ .split("\n")
+ .filter(Boolean)
+ .map(x => x.trim())
+ .reverse();
+ for (const line of lines) {
+ if (line.startsWith("at")) {
+ stack.push(line);
+ } else {
+ error = line;
+ break;
+ }
+ }
+ if (!error) {
+ throw new Error(`${prefix}Runtime failed with no error. Expecting "${run.error}"`);
+ }
+ expect(error).toBe(run.error);
+
+ if (run.errorLineMatch) {
+ const stackTraceLine = stack.pop()!;
+ const match = /at (.*):(\d+):(\d+)$/.exec(stackTraceLine);
+ if (match) {
+ const line = readFileSync(match[1], "utf-8").split("\n")[+match[2] - 1];
+ if (!run.errorLineMatch.test(line)) {
+ throw new Error(`${prefix}Source code "${line}" does not match expression ${run.errorLineMatch}`);
+ }
+ } else {
+ throw new Error(prefix + "Could not trace error.");
+ }
+ }
+ }
+ } else if (!success) {
+ throw new Error(prefix + "Runtime failed\n" + stdout!.toString("utf-8") + "\n" + stderr!.toString("utf-8"));
+ }
+
+ if (run.stdout !== undefined) {
+ const result = stdout!.toString("utf-8").trim();
+ const expected = dedent(run.stdout).trim();
+ if (expected !== result) {
+ console.log({ file });
+ }
+ expect(result).toBe(expected);
+ }
+ }
+ }
+}
+
+/** Shorthand for test and expectBundled. See `expectBundled` for what this does.
+ */
+export function itBundled(id: string, opts: BundlerTestInput) {
+ const { it } = testForFile(callerSourceOrigin());
+
+ if (FILTER && id !== FILTER) {
+ return;
+ } else if (!FILTER) {
+ try {
+ expectBundled(id, opts, true);
+ } catch (error) {
+ it.skip(id, () => {});
+ return;
+ }
+ }
+
+ it(id, () => expectBundled(id, opts));
+}
+itBundled.skip = (id: string, opts: BundlerTestInput) => {
+ const { it } = testForFile(callerSourceOrigin());
+ return it.skip(id, () => expectBundled(id, opts));
+};
+
+/** version of test that applies filtering */
+export function bundlerTest(id: string, cb: () => void) {
+ if (FILTER && id !== FILTER) {
+ return;
+ }
+ const { it } = testForFile(callerSourceOrigin());
+ it(id, cb);
+}
+bundlerTest.skip = (id: string, cb: any) => {
+ const { it } = testForFile(callerSourceOrigin());
+ return it.skip(id, cb);
+};
+
+function formatError(err: { file: string; error: string; line?: string; col?: string }) {
+ return `${err.file}${err.line ? " :" + err.line : ""}${err.col ? ":" + err.col : ""}: ${err.error}`;
+}
diff --git a/test/bundler/run-single-bundler-test.sh b/test/bundler/run-single-bundler-test.sh
new file mode 100755
index 000000000..5e9484dae
--- /dev/null
+++ b/test/bundler/run-single-bundler-test.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+if [ -z "$1" ]; then
+ echo "Usage: $0 <test name> <use_esbuild>"
+ echo "If you pass the second argument as anything, this will use esbuild instead of bun build."
+ exit 1
+fi
+
+__dirname="$(dirname "$0")"
+cd "$__dirname"
+
+clear
+
+printf "bun build test helper: $@"
+printf "\n\n"
+
+export BUN_BUNDLER_TEST_DEBUG=1
+export BUN_BUNDLER_TEST_FILTER=$1
+if [ -n "$2" ]; then
+ export BUN_BUNDLER_TEST_USE_ESBUILD=1
+fi
+
+export FORCE_COLOR=1
+bun test bundler_ esbuild/ 2>&1 \
+ | perl -ne 'print unless /^\e\[0m$/' \
+ | grep -v -P '\x1b\[0m\x1b\[33m-\x1b\[2m \x1b\[0m\x1b\[2mbundler' \
+ | grep -v ".test.ts:$" \
+ | tee /tmp/run-single-bundler-test.txt \
+ | grep "root:" -v
+
+symlinkDir=$(cat /tmp/run-single-bundler-test.txt | grep "root:" | cut -d " " -f 2)
+rm /tmp/run-single-bundler-test.txt
+rm $__dirname/out -rf
+if [ -e "$symlinkDir" ]; then
+ ln -s "$symlinkDir" $__dirname/out
+fi
diff --git a/test/fixtures/bundle/trivial/fn.js b/test/fixtures/bundle/trivial/fn.js
new file mode 100644
index 000000000..467f11c0b
--- /dev/null
+++ b/test/fixtures/bundle/trivial/fn.js
@@ -0,0 +1,3 @@
+export function fn() {
+ return 42;
+}
diff --git a/test/fixtures/bundle/trivial/index.js b/test/fixtures/bundle/trivial/index.js
new file mode 100644
index 000000000..15a8c7af4
--- /dev/null
+++ b/test/fixtures/bundle/trivial/index.js
@@ -0,0 +1,5 @@
+const NS = import("./fn.js");
+
+NS.then(({ fn }) => {
+ console.log(fn(42));
+});
diff --git a/test/js/bun/resolve/import-meta.test.js b/test/js/bun/resolve/import-meta.test.js
index c4bbdb1a6..56901dd52 100644
--- a/test/js/bun/resolve/import-meta.test.js
+++ b/test/js/bun/resolve/import-meta.test.js
@@ -1,10 +1,10 @@
-import { it, expect } from "bun:test";
-import { mkdirSync, rmSync, writeFileSync } from "node:fs";
-import * as Module from "node:module";
-import sync from "./require-json.json";
import { spawnSync } from "bun";
+import { expect, it } from "bun:test";
import { bunEnv, bunExe } from "harness";
+import { mkdirSync, rmSync, writeFileSync } from "node:fs";
+import * as Module from "node:module";
import { join } from "node:path";
+import sync from "./require-json.json.js";
const { path, dir } = import.meta;
diff --git a/test/js/deno/html/blob.test.ts b/test/js/deno/html/blob.test.ts
new file mode 100644
index 000000000..af1e7f6e8
--- /dev/null
+++ b/test/js/deno/html/blob.test.ts
@@ -0,0 +1,114 @@
+// Updated: Wed, 08 Mar 2023 00:55:15 GMT
+// URL: https://raw.githubusercontent.com/denoland/deno/main/cli/tests/unit/blob_test.ts
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+import { assert, assertEquals, assertStringIncludes } from "deno:harness";
+import { concat } from "deno:harness";
+
+Deno.test(function blobString() {
+ const b1 = new Blob(["Hello World"]);
+ const str = "Test";
+ const b2 = new Blob([b1, str]);
+ assertEquals(b2.size, b1.size + str.length);
+});
+
+Deno.test(function blobBuffer() {
+ const buffer = new ArrayBuffer(12);
+ const u8 = new Uint8Array(buffer);
+ const f1 = new Float32Array(buffer);
+ const b1 = new Blob([buffer, u8]);
+ assertEquals(b1.size, 2 * u8.length);
+ const b2 = new Blob([b1, f1]);
+ assertEquals(b2.size, 3 * u8.length);
+});
+
+Deno.test(function blobSlice() {
+ const blob = new Blob(["Deno", "Foo"]);
+ const b1 = blob.slice(0, 3, "Text/HTML");
+ assert(b1 instanceof Blob);
+ assertEquals(b1.size, 3);
+ assertEquals(b1.type, "text/html");
+ const b2 = blob.slice(-1, 3);
+ assertEquals(b2.size, 0);
+ const b3 = blob.slice(100, 3);
+ assertEquals(b3.size, 0);
+ const b4 = blob.slice(0, 10);
+ assertEquals(b4.size, blob.size);
+});
+
+Deno.test(function blobInvalidType() {
+ const blob = new Blob(["foo"], {
+ type: "\u0521",
+ });
+
+ assertEquals(blob.type, "");
+});
+
+Deno.test(function blobShouldNotThrowError() {
+ let hasThrown = false;
+
+ try {
+ // deno-lint-ignore no-explicit-any
+ const options1: any = {
+ ending: "utf8",
+ hasOwnProperty: "hasOwnProperty",
+ };
+ const options2 = Object.create(null);
+ new Blob(["Hello World"], options1);
+ new Blob(["Hello World"], options2);
+ } catch {
+ hasThrown = true;
+ }
+
+ assertEquals(hasThrown, false);
+});
+
+/* TODO https://github.com/denoland/deno/issues/7540
+Deno.test(function nativeEndLine() {
+ const options = {
+ ending: "native",
+ } as const;
+ const blob = new Blob(["Hello\nWorld"], options);
+
+ assertEquals(blob.size, Deno.build.os === "windows" ? 12 : 11);
+});
+*/
+
+Deno.test(async function blobText() {
+ const blob = new Blob(["Hello World"]);
+ assertEquals(await blob.text(), "Hello World");
+});
+
+Deno.test(async function blobStream() {
+ const blob = new Blob(["Hello World"]);
+ const stream = blob.stream();
+ assert(stream instanceof ReadableStream);
+ const reader = stream.getReader();
+ let bytes = new Uint8Array();
+ const read = async (): Promise<void> => {
+ const { done, value } = await reader.read();
+ if (!done && value) {
+ bytes = concat(bytes, value);
+ return read();
+ }
+ };
+ await read();
+ const decoder = new TextDecoder();
+ assertEquals(decoder.decode(bytes), "Hello World");
+});
+
+Deno.test(async function blobArrayBuffer() {
+ const uint = new Uint8Array([102, 111, 111]);
+ const blob = new Blob([uint]);
+ assertEquals(await blob.arrayBuffer(), uint.buffer);
+});
+
+Deno.test(function blobConstructorNameIsBlob() {
+ const blob = new Blob();
+ assertEquals(blob.constructor.name, "Blob");
+});
+
+Deno.test(function blobCustomInspectFunction() {
+ const blob = new Blob();
+ assertEquals(Deno.inspect(blob), `Blob { size: 0, type: "" }`);
+ assertStringIncludes(Deno.inspect(Blob.prototype), "Blob");
+});
diff --git a/test/package.json b/test/package.json
index bd2295ddd..7df5401be 100644
--- a/test/package.json
+++ b/test/package.json
@@ -1,12 +1,15 @@
{
"name": "test",
- "devDependencies": {},
+ "devDependencies": {
+ "@types/dedent": "^0.7.0"
+ },
"dependencies": {
"@swc/core": "^1.3.38",
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
"bktree-fast": "^0.0.7",
"body-parser": "^1.20.2",
+ "dedent": "^0.7.0",
"esbuild": "^0.17.11",
"express": "^4.18.2",
"iconv-lite": "^0.6.3",