Files
bun.sh/test/bundler/bundler_edgecase.test.ts
Jarred Sumner 9411c62756 Make the bundler tests use the API by default in most cases (#22646)
### What does this PR do?

This branch:
> Ran 1600 tests across 46 files. [63.24s]

Main:
> Ran 1600 tests across 46 files. [137.05s]

This makes the bundler tests run about 60 seconds faster 

### How did you verify your code works?
2025-09-14 02:27:53 -07:00

2385 lines
58 KiB
TypeScript

import { describe, expect } from "bun:test";
import { isBroken, isWindows } from "harness";
import { join } from "node:path";
import { itBundled } from "./expectBundled";
describe("bundler", () => {
itBundled("edgecase/EmptyFile", {
files: {
"/entry.js": "",
},
});
itBundled("edgecase/EmptyCommonJSModule", {
files: {
"/entry.js": /* js */ `
import * as module from './module.cjs';
console.log(typeof module)
`,
"/module.cjs": /* js */ ``,
},
run: {
stdout: "object",
},
});
itBundled("edgecase/NestedRedirectToABuiltin", {
files: {
"/entry.js": /* js */ `
import * as path from './module.cjs';
console.log(path.join('a', 'b'))
`,
"/module.cjs": /* js */ `
module.exports = require('./2nd')
`,
"/2nd.js": /* js */ `
module.exports = require('path')
`,
},
target: "bun",
run: {
stdout: join("a", "b"),
},
});
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" },
});
itBundled("edgecase/ImportStarSyntaxErrorBug", {
// bug: 'import {ns}, * as import_x from "x";'
files: {
"/entry.js": /* js */ `
export {ns} from 'x'
export * as ns2 from 'x'
`,
},
external: ["x"],
runtimeFiles: {
"/node_modules/x/index.js": `export const ns = 1`,
},
run: true,
});
itBundled("edgecase/BunPluginTreeShakeImport", {
todo: true,
// This only appears at runtime and not with bun build, even with --no-bundle
files: {
"/entry.ts": /* js */ `
import { A, B } from "./somewhere-else";
import { plugin } from "bun";
plugin(B());
new A().chainedMethods();
`,
"/somewhere-else.ts": /* js */ `
export class A {
chainedMethods() {
console.log("hey");
}
}
export function B() {
return { name: 'hey' }
}
`,
},
minifySyntax: true,
target: "bun",
run: { file: "/entry.ts" },
});
itBundled("edgecase/TemplateStringIssue622", {
files: {
"/entry.ts": /* js */ `
capture(\`\\?\`);
capture(hello\`\\?\`);
`,
},
capture: ["`?`", "hello`\\?`"],
target: "bun",
});
// https://github.com/oven-sh/bun/issues/2699
itBundled("edgecase/ImportNamedFromExportStarCJS", {
files: {
"/entry.js": /* js */ `
import { foo } from './foo';
console.log(foo);
`,
"/foo.js": /* js */ `
export * from './bar.cjs';
`,
"/bar.cjs": /* js */ `
module.exports = { foo: 'bar' };
`,
},
run: {
stdout: "bar",
},
});
itBundled("edgecase/NodeEnvDefaultUnset", {
files: {
"/entry.js": /* js */ `
capture(process.env.NODE_ENV);
capture(process.env.NODE_ENV === 'production');
capture(process.env.NODE_ENV === 'development');
`,
},
target: "browser",
capture: ['"development"', "false", "true"],
env: {
// undefined will ensure this variable is not passed to the bundler
NODE_ENV: undefined,
},
});
itBundled("edgecase/NodeEnvDefaultDevelopment", {
files: {
"/entry.js": /* js */ `
capture(process.env.NODE_ENV);
capture(process.env.NODE_ENV === 'production');
capture(process.env.NODE_ENV === 'development');
`,
},
target: "browser",
capture: ['"development"', "false", "true"],
env: {
NODE_ENV: "development",
},
});
itBundled("edgecase/NodeEnvDefaultProduction", {
files: {
"/entry.js": /* js */ `
capture(process.env.NODE_ENV);
capture(process.env.NODE_ENV === 'production');
capture(process.env.NODE_ENV === 'development');
`,
},
target: "browser",
capture: ['"production"', "true", "false"],
env: {
NODE_ENV: "production",
},
});
itBundled("edgecase/NodeEnvOptionalChaining", {
todo: true,
files: {
"/entry.js": /* js */ `
capture(process?.env?.NODE_ENV);
capture(process?.env?.NODE_ENV === 'production');
capture(process?.env?.NODE_ENV === 'development');
capture(process.env?.NODE_ENV);
capture(process.env?.NODE_ENV === 'production');
capture(process.env?.NODE_ENV === 'development');
capture(process?.env.NODE_ENV);
capture(process?.env.NODE_ENV === 'production');
capture(process?.env.NODE_ENV === 'development');
`,
},
target: "browser",
capture: ['"development"', "false", "true", '"development"', "false", "true", '"development"', "false", "true"],
env: {
NODE_ENV: "development",
},
});
itBundled("edgecase/StarExternal", {
files: {
"/entry.js": /* js */ `
import { foo } from './foo';
import { bar } from './bar';
console.log(foo);
`,
},
external: ["*"],
});
itBundled("edgecase/ImportNamespaceAndDefault", {
files: {
"/entry.js": /* js */ `
import def2, * as ns2 from './c'
console.log(def2, JSON.stringify(ns2))
`,
"/c.js": /* js */ `
export const ns = 2
export const def2 = 3
export default 1
`,
},
runtimeFiles: {},
run: {
stdout: '1 {"ns":2,"default":1,"def2":3}',
},
});
itBundled("edgecase/ExternalES6ConvertedToCommonJSSimplified", {
todo: true,
files: {
"/entry.js": /* js */ `
console.log(JSON.stringify(require('./e')));
`,
"/e.js": `export * from 'x'`,
},
external: ["x"],
runtimeFiles: {
"/node_modules/x/index.js": /* js */ `
export const ns = 123
export const ns2 = 456
`,
},
run: {
stdout: `
{"ns":123,"ns2":456}
`,
},
});
itBundled("edgecase/ImportTrailingSlash", {
files: {
"/entry.js": /* js */ `
import "slash/"
`,
"/node_modules/slash/index.js": /* js */ `console.log(1)`,
},
run: {
stdout: "1",
},
});
itBundled("edgecase/ValidLoaderSeenAsInvalid", {
files: {
"/entry.js": /* js */ `console.log(1)`,
},
outdir: "/out",
loader: {
".a": "file",
".b": "text",
".c": "toml",
".d": "json",
".e": "js",
".f": "ts",
".g": "jsx",
".h": "tsx",
// ".i": "wasm",
// ".j": "napi",
// ".k": "base64",
// ".l": "dataurl",
// ".m": "binary",
// ".n": "empty",
// ".o": "copy",
},
});
itBundled("edgecase/InvalidLoaderSegfault", {
files: {
"/entry.js": /* js */ `console.log(1)`,
},
outdir: "/out",
backend: "cli",
loader: {
".cool": "wtf",
},
bundleErrors: {
"<bun>": ['invalid loader "wtf", expected one of:'],
},
});
itBundled("edgecase/ScriptTagEscape", {
todo: true,
files: {
"/entry.js": /* js */ `
console.log('<script></script>');
console.log(await import('./text-file.txt'))
`,
"/text-file.txt": /* txt */ `
<script></script>
`,
},
outdir: "/out",
onAfterBundle(api) {
try {
expect(api.readFile("/out/entry.js")).not.toContain("</script>");
} catch (error) {
console.error("Bundle contains </script> which will break if this bundle is placed in a script tag.");
throw error;
}
},
});
itBundled("edgecase/JSONDefaultImport", {
files: {
"/entry.js": /* js */ `
import def from './test.json'
console.log(JSON.stringify(def))
`,
"/test.json": `{ "hello": 234, "world": 123 }`,
},
run: {
stdout: '{"hello":234,"world":123}',
},
});
itBundled("edgecase/JSONDefaultKeyImport", {
files: {
"/entry.js": /* js */ `
import def from './test.json'
console.log(def.hello)
`,
"/test.json": `{ "hello": 234, "world": "REMOVE" }`,
},
run: {
stdout: "234",
},
});
itBundled("edgecase/JSONDefaultAndNamedImport", {
todo: true,
files: {
"/entry.js": /* js */ `
import def from './test.json'
import { hello } from './test.json'
console.log(def.hello, hello)
`,
"/test.json": `{ "hello": 234, "world": "REMOVE" }`,
},
dce: true,
run: {
stdout: "234 234",
},
});
itBundled("edgecase/JSONWithDefaultKey", {
files: {
"/entry.js": /* js */ `
import def from './test.json'
console.log(JSON.stringify(def))
`,
"/test.json": `{ "default": 234 }`,
},
dce: true,
run: {
stdout: '{"default":234}',
},
});
itBundled("edgecase/JSONWithDefaultKeyNamespace", {
todo: true,
files: {
"/entry.js": /* js */ `
import * as ns from './test.json'
console.log(JSON.stringify(ns))
`,
"/test.json": `{ "default": 234 }`,
},
dce: true,
run: {
stdout: '{"default":234}',
},
});
itBundled("edgecase/RequireUnknownExtension", {
files: {
"/entry.js": /* js */ `
require('./x.aaaa')
`,
"/x.aaaa": `x`,
},
outdir: "/out",
});
itBundled("edgecase/PackageJSONDefaultConditionRequire", {
files: {
"/entry.js": /* js */ `
const boop = require('boop')
console.log(boop)
`,
"/node_modules/boop/package.json": /* json */ `
{
"name": "boop",
"exports": {
".": {
"boop-server": "./ignore.js",
"default": "./boop.js"
}
}
}
`,
"/node_modules/boop/boop.js": /* js */ `
module.exports = 123
`,
},
run: {
stdout: "123",
},
});
itBundled("edgecase/PackageJSONDefaultConditionImport", {
files: {
"/entry.js": /* js */ `
import React from 'boop'
console.log(React)
`,
// NOTE: this test fails if the package name is "react"
// most likely an issue with commonjs unwrapping.
"/node_modules/boop/package.json": /* json */ `
{
"name": "boop",
"exports": {
".": {
"react-server": "./ignore.js",
"default": "./boop.js"
}
}
}
`,
"/node_modules/boop/boop.js": /* js */ `
export default 123
`,
},
run: {
stdout: "123",
},
});
itBundled("edgecase/TSConfigPathsStarOnlyInLeft", {
files: {
"/entry.ts": /* ts */ `
import test0 from 'test0/hello'
console.log(test0)
`,
"/tsconfig.json": /* json */ `
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"test0/*": ["./test0-success.ts"]
}
}
}
`,
"/test0-success.ts": `export default 'success'`,
},
run: {
stdout: "success",
},
});
itBundled("edgecase/TSConfigPathStarAnywhere", {
todo: true,
files: {
"/entry.ts": /* ts */ `
import test0 from 'test3/foo'
console.log(test0)
`,
"/tsconfig.json": /* json */ `
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"t*t3/foo": ["./test3-succ*s.ts"],
}
}
}
`,
"/test3-success.ts": `export default 'success'`,
},
run: {
stdout: "success",
},
});
itBundled("edgecase/StaticClassNameIssue2806", {
files: {
"/entry.ts": /* ts */ `
new class C {
set baz(x) {
C.foo = x;
C.bar;
}
static get bar() {
console.log(C.foo);
}
}().baz = "PASS";
new class C {
set baz(x) {
C.foo = x;
C.bar;
}
static get bar() {
console.log(C.foo);
}
}().baz = "Hello World";
`,
},
minifyIdentifiers: true,
run: {
stdout: "PASS\nHello World",
},
});
itBundled("edgecase/DCEVarRedeclarationIssue2814A", {
files: {
"/entry.ts": /* ts */ `
var a = 1;
if (false) {
var a;
}
console.log(a);
`,
},
target: "bun",
run: {
stdout: `1`,
},
});
itBundled("edgecase/DCEVarRedeclarationIssue2814B", {
files: {
"/entry.ts": /* ts */ `
var a = 1;
switch ("foo") {
case "foo":
var a;
}
console.log(a);
`,
},
target: "bun",
run: {
stdout: `1`,
},
});
itBundled("edgecase/DCEVarRedeclarationIssue2814C", {
files: {
"/entry.ts": /* ts */ `
"use strict";
var a = 1;
{
var a;
}
console.log(a);
`,
},
target: "bun",
run: {
stdout: `1`,
},
});
itBundled("edgecase/DCEVarRedeclarationIssue2814", {
files: {
"/entry.ts": /* ts */ `
"use strict";
var a = 1, b = 2;
switch (b++) {
case b:
var c = a;
var a;
break;
}
console.log(a);
var x = 123, y = 45;
switch (console) {
case 456:
var x = 789, y = 0;
}
var y = 67;
console.log(x, y);
var z = 123;
switch (console) {
default:
var z = typeof z;
}
console.log(z);
var A = 1, B = 2;
switch (A) {
case A:
var B;
break;
case B:
break;
}
console.log(B);
`,
},
target: "bun",
run: {
stdout: `
1
123 67
number
2
`,
},
});
itBundled("edgecase/DCEVarRedeclarationIssue2815", {
todo: true,
files: {
"/entry.ts": /* ts */ `
var x = 1;
try {
console.blog;
} catch (x) {
var x = 2;
}
console.log(x);
var e = 3;
try {
console.log("try2");
} catch (e) {
var e = 4;
}
console.log(e);
try {
var z = 5;
throw "try3";
} catch (w) {
z += w;
var w = 6;
}
console.log(z);
var c = 8;
try {
"try4";
} catch (c) {
var c = 9;
}
console.log(c);
`,
},
target: "bun",
run: {
stdout: `
1
123 67
number
2
`,
},
});
itBundled("edgecase/AbsolutePathShouldNotResolveAsRelative", {
files: {
"/entry.js": /* js */ `
console.log(1);
`,
},
entryPointsRaw: ["/entry.js"],
bundleErrors: {
"<bun>": ['ModuleNotFound resolving "/entry.js" (entry point)'],
},
});
itBundled("edgecase/AssetEntryPoint", {
files: {
"/entry.zig": `
const std = @import("std");
pub fn main() void {
std.log.info("Hello, world!\\n", .{});
}
`,
},
outdir: "/out",
entryPointsRaw: ["./entry.zig"],
runtimeFiles: {
"/exec.js": `
import assert from 'node:assert';
import the_path from './out/entry.js';
assert.strictEqual(the_path, './entry-z5artd5z.zig');
`,
},
run: {
file: "./exec.js",
},
});
itBundled("edgecase/ExportDefaultUndefined", {
files: {
"/entry.ts": /* ts */ `
export const a = 1;
`,
},
target: "bun",
});
itBundled("edgecase/RuntimeExternalRequire", {
files: {
"/entry.ts": /* ts */ `
console.log(require("hello-1").type);
`,
},
external: ["hello-1"],
target: "bun",
runtimeFiles: {
"/node_modules/hello-1/require.js": `export const type = "require";`,
"/node_modules/hello-1/package.json": /* json */ `
{
"type": "module",
"exports": {
".": {
"require": "./require.js",
}
}
}
`,
},
run: {
stdout: `
require
`,
},
});
itBundled("edgecase/RuntimeExternalImport", {
todo: true,
files: {
"/entry.ts": /* ts */ `
import { type as a1 } from 'hello-1';
import { type as a2 } from 'hello-2';
import { type as a3 } from 'hello-3';
console.log(a1, a2, a3);
const b1 = require('hello-1').type;
const b2 = require('hello-2').type;
const b3 = require('hello-3').type;
console.log(b1, b2, b3);
`,
},
external: ["hello-1", "hello-2", "hello-3"],
target: "bun",
runtimeFiles: {
"/node_modules/hello-1/node.js": `export const type = "node";`,
"/node_modules/hello-1/bun.js": `export const type = "bun";`,
"/node_modules/hello-1/package.json": /* json */ `
{
"type": "module",
"exports": {
".": {
"node": "./node.js",
"bun": "./bun.js"
}
}
}
`,
"/node_modules/hello-2/node.js": `export const type = "node";`,
"/node_modules/hello-2/bun.js": `export const type = "bun";`,
"/node_modules/hello-2/package.json": /* json */ `
{
"type": "module",
"exports": {
".": {
"bun": "./bun.js",
"node": "./node.js"
}
}
}
`,
"/node_modules/hello-3/import.js": `export const type = "import";`,
"/node_modules/hello-3/require.js": `exports.type = "require";`,
"/node_modules/hello-3/package.json": /* json */ `
{
"type": "module",
"exports": {
".": {
"require": "./require.js",
"import": "./import.js",
}
}
}
`,
},
run: {
stdout: `
bun bun import
bun bun import
`,
},
});
itBundled("edgecase/RuntimeExternalImport2", {
todo: true,
files: {
"/entry.ts": /* ts */ `
import t from 'hello';
console.log(t);
`,
},
external: ["hello"],
target: "bun",
runtimeFiles: {
"/node_modules/hello/index.js": /* js */ `
export const hello = "Hello World";
`,
},
run: {
stdout: "Hello World",
},
});
itBundled("edgecase/AssetPublicPath", {
files: {
"/entry.ts": /* ts */ `
import hello from "./hello.file";
console.log(hello);
`,
"/hello.file": "Hello World",
},
outdir: "/out",
publicPath: "/www",
run: {},
});
itBundled("edgecase/ImportDefaultInDirectory", {
files: {
"/a/file.js": `
import def from './def'
console.log(def)
`,
"/a/def.js": `
export default 1;
console.log('inner');
`,
},
run: {
file: "/out.js",
stdout: "inner\n1",
},
});
itBundled("edgecase/RequireVarThenExport", {
files: {
"/entry.js": /* js */ `
import { version } from './react';
console.log(version);
`,
"/react.js": /* js */ `
const a = require('./library.js');
exports.version = a.version;
`,
"/library.js": /* js */ `
exports.version = '0.6.0';
`,
},
target: "bun",
run: {
stdout: `0.6.0`,
},
});
itBundled("edgecase/OverwriteInputWithOutdir", {
todo: true,
files: {
"/entry.js": /* js */ `
import { version } from './library';
console.log(version);
`,
"/library.js": /* js */ `
exports.version = '0.6.0';
`,
},
outdir: "/",
bundleErrors: {
"<bun>": ['Refusing to overwrite input file "/entry.js"'],
},
});
itBundled("edgecase/OverwriteInputWithOutfile", {
todo: true,
files: {
"/entry.js": /* js */ `
import { version } from './library';
console.log(version);
`,
"/library.js": /* js */ `
exports.version = '0.6.0';
`,
},
outfile: "/entry.js",
bundleErrors: {
"<bun>": ['Refusing to overwrite input file "/entry.js"'],
},
});
itBundled("edgecase/OverwriteInputNonEntrypoint", {
todo: true,
files: {
"/entry.js": /* js */ `
import { version } from './library';
console.log(version);
`,
"/library.js": /* js */ `
exports.version = '0.6.0';
`,
},
outfile: "/entry.js",
bundleErrors: {
"<bun>": ['Refusing to overwrite input file "/entry.js"'],
},
});
itBundled("edgecase/ModuleExportsFunctionIssue2911", {
files: {
"/entry.js": /* js */ `
const fn = require('fresh');
console.log(fn());
const fn2 = require('./not_in_node_modules');
console.log(fn2());
import fn3 from 'fresh';
console.log(fn());
import fn4 from './not_in_node_modules';
console.log(fn2());
`,
"/node_modules/fresh/index.js": /* js */ `
module.exports = function() {
return 'it worked';
}
`,
"/not_in_node_modules.js": /* js */ `
module.exports = function() {
return 'it worked';
}
`,
},
run: {
stdout: "it worked\nit worked\nit worked\nit worked",
},
});
itBundled("edgecase/IsBuffer1", {
files: {
"/entry.js": /* js */ `
import isBuffer from 'lodash-es/isBuffer';
if(isBuffer !== 1) throw 'fail';
console.log('pass');
`,
"/node_modules/lodash-es/isBuffer.js": /* js */ `
var freeExports = typeof exports == 'object';
// this is using the 'freeExports' variable but giving a predictable outcome
const isBuffer = freeExports ? 1 : 1;
export default isBuffer;
`,
},
run: {
stdout: "pass",
},
});
itBundled("edgecase/TS_LessThanAmbiguity", {
files: {
"/entry.ts": `
function expectArrow(item) {
if(typeof item !== 'function') {
throw new Error('Expected arrow function');
}
}
function expectTypeCast(item) {
if(typeof item !== 'number') {
throw new Error('Expected arrow function');
}
}
const x = 1;
expectTypeCast(<A>(x));
expectTypeCast(<[]>(x));
expectTypeCast(<A[]>(x));
expectArrow(<A>(x) => {})
expectArrow(<A, B>(x) => {})
expectArrow(<A = B>(x) => {})
expectArrow(<A extends B>(x) => {})
expectArrow(<const A extends B>(x) => {})
console.log('pass');
`,
},
run: {
stdout: "pass",
},
});
itBundled("edgecase/TSX_LessThanAmbiguity", {
files: {
"/entry.tsx": `
function expectJSX(item) {
if(typeof item !== 'object') {
throw new Error('Expected JSX');
}
}
function expectArrow(item) {
if(typeof item !== 'function') {
throw new Error('Expected arrow function');
}
}
const A = 1;
expectJSX(<A>(x) ...</A>);
expectJSX(<A extends>(x) ... </A>);
expectJSX(<A extends={false}>(x) ... </A>);
expectJSX(<const A extends>(x) ...</const>);
expectJSX(<const extends T>(x) ...</const>);
expectJSX(<const A B>(x) ...</const>);
expectJSX(<const A B C>(x) ...</const>);
expectArrow(<A, B>(x) => {});
expectArrow(<A extends B>(x) => {});
expectArrow(<const A extends B>(x) => {});
console.log('pass');
`,
"/node_modules/react/jsx-dev-runtime.js": `
export function jsxDEV(type, props, key, isStaticChildren, source, self) {
return {};
}
`,
},
run: {
stdout: "pass",
},
});
itBundled("edgecase/IsBuffer2", {
files: {
"/entry.js": /* js */ `
import isBuffer from 'lodash-es/isBuffer';
if(isBuffer !== 1) throw 'fail';
console.log('pass');
`,
"/node_modules/lodash-es/package.json": /* json */ `
{ "name": "lodash-es", "type": "module"}
`,
"/node_modules/lodash-es/isBuffer.js": /* js */ `
var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;
var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;
// this is using the 'freeExports' variable but giving a predictable outcome
const isBuffer = [freeExports, freeModule] ? 1 : 1;
export default isBuffer;
`,
},
run: {
stdout: "pass",
},
});
itBundled("edgecase/YieldKeyword", {
files: {
"/entry.js": /* js */ `
function* foo() {
yield 1;
[yield];
yield yield yield;
[yield * 2];
[yield (yield)];
{ x: yield };
(yield).hello
yield+1
}
`,
},
});
itBundled("edgecase/UsingWithSixImports", {
files: {
"/entry.js": /* js */ `
import { Database } from 'bun:sqlite';
import 'bun';
import 'bun:ffi';
import 'bun:jsc';
import 'node:assert';
import 'bun:test';
using a = new Database();
export { a };
`,
},
target: "bun",
});
itBundled("edgecase/EmitInvalidSourceMap1", {
files: {
"/src/index.ts": /* ts */ `
const y = await import("./second.mts");
import * as z from "./third.mts";
const v = await import("./third.mts");
console.log(z, v, y);
`,
"/src/second.mts": /* ts */ `
export default "swag";
`,
"/src/third.mts": /* ts */ `
export default "bun";
`,
},
outdir: "/out",
target: "bun",
sourceMap: "external",
minifySyntax: true,
minifyIdentifiers: true,
minifyWhitespace: true,
splitting: true,
});
// chunk-concat weaved mappings together incorrectly causing the `console`
// token to be -2, thus breaking the rest of the mappings in the file
itBundled("edgecase/EmitInvalidSourceMap2", {
files: {
"/entry.js": `
import * as react from "react";
console.log(react);
`,
"/node_modules/react/index.js": `
var _ = module;
sideEffect(() => {});
`,
},
outdir: "/out",
sourceMap: "external",
minifySyntax: true,
minifyIdentifiers: true,
minifyWhitespace: true,
snapshotSourceMap: {
"entry.js.map": {
files: ["../node_modules/react/index.js", "../entry.js"],
mappingsExactMatch: "qYACA,WAAW,IAAQ,EAAE,ICDrB,eACA,QAAQ,IAAI,CAAK",
},
},
});
// chunk-concat forgets to de-duplicate source indicies
// chunk-concat ignores all but the first instance of a chunk
itBundled("edgecase/EmitInvalidSourceMap2", {
files: {
"/entry.js": `
const a = new TextEncoder();
console.log('hey!')
const d = new TextEncoder();
const b = { hello: 'world' };
const c = new Set([
]);
console.log('hey!')
console.log('hey!')
console.log('hey!')
console.log('hey!')
`,
},
outdir: "/out",
sourceMap: "external",
minifySyntax: true,
minifyIdentifiers: true,
minifyWhitespace: true,
snapshotSourceMap: {
"entry.js.map": {
files: ["../entry.js"],
mappingsExactMatch:
"AACQ,QAAQ,IAAI,MAAM,EAOlB,QAAQ,IAAI,MAAM,EAClB,QAAQ,IAAI,MAAM,EAClB,QAAQ,IAAI,MAAM,EAClB,QAAQ,IAAI,MAAM",
},
},
});
itBundled("edgecase/NoUselessConstructorTS", {
files: {
"/entry.ts": `
class A {
constructor(...args) {
console.log(JSON.stringify({ args, self: this }));
}
field = 1;
}
class B extends A {}
class C extends A { field = 2 }
class D extends A { public field = 3 }
class E extends A { constructor(public y: number, a) { super(a); }; public field = 4 }
new A("arg1", "arg2");
new B("arg1", "arg2");
new C("arg1", "arg2");
new D("arg1", "arg2");
new E("arg1", "arg2");
`,
},
run: {
stdout: `
{"args":["arg1","arg2"],"self":{"field":1}}
{"args":["arg1","arg2"],"self":{"field":1}}
{"args":["arg1","arg2"],"self":{"field":1}}
{"args":["arg1","arg2"],"self":{"field":1}}
{"args":["arg2"],"self":{"field":1}}
`,
},
onAfterBundle(api) {
const content = api.readFile("out.js");
const count = content.split("constructor").length - 1;
expect(count, "should only emit two constructors: " + content).toBe(2);
},
});
itBundled("edgecase/EnumInliningRopeStringPoison", {
files: {
"/entry.ts": `
const enum A1 {
B = "1" + "2",
C = "3" + B,
};
console.log(A1.B, A1.C);
const enum A2 {
B = "1" + "2",
C = ("3" + B) + "4",
};
console.log(A2.B, A2.C);
`,
},
run: {
stdout: "12 312\n12 3124",
},
});
itBundled("edgecase/ProtoNullProtoInlining", {
files: {
"/entry.ts": `
console.log({ __proto__: null }.__proto__ !== void 0)
`,
},
run: {
stdout: "false",
},
});
itBundled("edgecase/ImportOptionsArgument", {
files: {
"/entry.js": `
import('ext', { with: { get ''() { KEEP } } })
.then(function (error) {
console.log(error);
});
`,
},
dce: true,
external: ["ext"],
target: "bun",
});
itBundled("edgecase/ConstantFoldingShiftOperations", {
files: {
"/entry.ts": `
capture(421 >> -542)
capture(421 >>> -542)
capture(1 << 32)
capture(1 >> 32)
capture(1 >>> 32)
capture(47849312 << 34)
capture(-9 >> 1)
capture(-5 >> 1)
`,
},
minifySyntax: true,
capture: ["105", "105", "1", "1", "1", "191397248", "-5", "-3"],
});
itBundled("edgecase/ConstantFoldingBitwiseCoersion", {
files: {
"/entry.ts": `
capture(0 | 0)
capture(12582912 | 0)
capture(0xc00000 | 0)
capture(Infinity | 0)
capture(-Infinity | 0)
capture(NaN | 0)
// u32 limits
capture(-4294967295 | 0)
capture(-4294967296 | 0)
capture(-4294967297 | 0)
capture(4294967295 | 0)
capture(4294967296 | 0)
capture(4294967297 | 0)
// i32 limits
capture(-2147483647 | 0)
capture(-2147483648 | 0)
capture(-2147483649 | 0)
capture(2147483647 | 0)
capture(2147483648 | 0)
capture(2147483649 | 0)
capture(0.5 | 0)
`,
},
minifySyntax: true,
capture: [
"0",
"12582912",
"12582912",
"0",
"0",
"0",
"1",
"0",
"-1",
"-1",
"0",
"1",
"-2147483647",
"-2147483648",
"2147483647",
"2147483647",
"-2147483648",
"-2147483647",
"0",
],
});
itBundled("edgecase/EnumInliningNanBoxedEncoding", {
files: {
"/main.ts": `
import { Enum } from './other.ts';
capture(Enum.a);
capture(Enum.b);
capture(Enum.c);
capture(Enum.d);
capture(Enum.e);
capture(Enum.f);
capture(Enum.g);
`,
"/other.ts": `
export const enum Enum {
a = 0,
b = NaN,
c = (0 / 0) + 1,
d = Infinity,
e = -Infinity,
f = 3e450,
// https://float.exposed/0xffefffffffffffff
g = -1.79769313486231570815e+308,
}
`,
},
minifySyntax: true,
capture: [
"0 /* a */",
"NaN /* b */",
"NaN /* c */",
"1 / 0 /* d */",
"-1 / 0 /* e */",
"1 / 0 /* f */",
// should probably fix this
"-179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 /* g */",
],
});
// Stack overflow possibility
itBundled("edgecase/AwsCdkLib", {
files: {
"entry.js": `import * as aws from ${JSON.stringify(require.resolve("aws-cdk-lib"))}; aws;`,
},
target: "bun",
run: true,
todo: isBroken && isWindows,
timeoutScale: 5,
});
itBundled("edgecase/PackageExternalDoNotBundleNodeModules", {
files: {
"/entry.ts": /* ts */ `
import { a } from "foo";
console.log(a);
`,
},
packages: "external",
target: "bun",
runtimeFiles: {
"/node_modules/foo/index.js": `export const a = "Hello World";`,
"/node_modules/foo/package.json": /* json */ `
{
"name": "foo",
"version": "2.0.0",
"main": "index.js"
}
`,
},
run: {
stdout: `
Hello World
`,
},
});
itBundled("edgecase/EntrypointWithoutPrefixSlashOrDotIsNotConsideredExternal#12734", {
files: {
"/src/entry.ts": /* ts */ `
import { helloWorld } from "./second.ts";
console.log(helloWorld);
`,
"/src/second.ts": /* ts */ `
export const helloWorld = "Hello World";
`,
},
root: "/src",
entryPointsRaw: ["src/entry.ts"],
packages: "external",
target: "bun",
run: {
file: "/src/entry.ts",
stdout: `
Hello World
`,
},
});
itBundled("edgecase/IntegerUnderflow#12547", {
files: {
"/entry.js": `
import { a } from 'external';
function func() {
const b = 1 + a.c;
return b;
}
`,
},
minifySyntax: true,
minifyWhitespace: true,
minifyIdentifiers: true,
external: ["external"],
onAfterBundle(api) {
// DCE is not yet able to eliminate the `a` or even the `as c`. Equivalent to esbuild as of 2024-07-15
api.expectFile("/out.js").toBe(`import{a as c}from"external";\n`);
},
});
itBundled("edgecase/TypeScriptNamespaceSiblingFunction", {
files: {
"/entry.ts": `
namespace X {
export function Y() {
return 2;
}
export namespace Y {
export const Z = 1;
}
}
console.log(X, X.Y(), X.Y.Z);
`,
},
run: {
stdout: "{\n Y: [Function: Y],\n} 2 1",
},
});
itBundled("edgecase/TypeScriptNamespaceSiblingClass", {
files: {
"/entry.ts": `
namespace X {
export class Y {
constructor(v) {
this.value = v;
}
toJSON() {
return this.value;
}
}
export namespace Y {
export const Z = 1;
}
}
console.log(X, new X.Y(2).toJSON(), X.Y.Z);
`,
},
run: {
stdout: "{\n Y: [class Y],\n} 2 1",
},
});
itBundled("edgecase/TypeScriptNamespaceSiblingEnum", {
files: {
"/entry.ts": `
namespace X {
export enum Y {
A,
B,
}
export namespace Y {
export const Z = 1;
}
}
console.log(JSON.stringify([X, X.Y.A, X.Y.Z]));
`,
},
run: {
stdout: '[{"Y":{"0":"A","1":"B","A":0,"B":1,"Z":1}},0,1]',
},
});
itBundled("edgecase/TypeScriptNamespaceSiblingVariable", {
files: {
"/entry.ts": `
namespace X {
export let Y = {};
export namespace Y {
export const Z = 1;
}
}
`,
},
bundleErrors: {
"/entry.ts": [`"Y" has already been declared`],
},
});
// This specifically only happens with 'export { ... } from ...' syntax
itBundled("edgecase/EsmSideEffectsFalseWithSideEffectsExportFrom", {
files: {
"/file1.js": `
import("./file2.js");
`,
"/file2.js": `
export { a } from './file3.js';
`,
"/file3.js": `
export function a(input) {
return 42;
}
console.log('side effect');
`,
"/package.json": `
{
"name": "my-package",
"sideEffects": false
}
`,
},
run: {
stdout: "side effect",
},
});
itBundled("edgecase/EsmSideEffectsFalseWithSideEffectsExportFromCodeSplitting", {
files: {
"/file1.js": `
import("./file2.js");
console.log('file1');
`,
"/file1b.js": `
import("./file2.js");
console.log('file2');
`,
"/file2.js": `
export { a } from './file3.js';
`,
"/file3.js": `
export function a(input) {
return 42;
}
console.log('side effect');
`,
"/package.json": `
{
"name": "my-package",
"sideEffects": false
}
`,
},
splitting: true,
outdir: "out",
entryPoints: ["./file1.js", "./file1b.js"],
run: [
{
file: "/out/file1.js",
stdout: "file1\nside effect",
},
{
file: "/out/file1b.js",
stdout: "file2\nside effect",
},
],
});
itBundled("edgecase/RequireSideEffectsFalseWithSideEffectsExportFrom", {
files: {
"/file1.js": `
require("./file2.js");
`,
"/file2.js": `
export { a } from './file3.js';
`,
"/file3.js": `
export function a(input) {
return 42;
}
console.log('side effect');
`,
"/package.json": `
{
"name": "my-package",
"sideEffects": false
}
`,
},
run: {
stdout: "side effect",
},
});
itBundled("edgecase/SideEffectsFalseWithSideEffectsExportFrom", {
files: {
"/file1.js": `
import("./file2.js");
`,
"/file2.js": `
import * as foo from './file3.js';
export default foo;
`,
"/file3.js": `
export function a(input) {
return 42;
}
console.log('side effect');
`,
"/package.json": `
{
"name": "my-package",
"sideEffects": false
}
`,
},
run: {
stdout: "side effect",
},
});
itBundled("edgecase/BuiltinWithTrailingSlash", {
files: {
"/entry.js": `
import * as process from 'process/';
console.log(JSON.stringify(process));
`,
"/node_modules/process/index.js": `
export default { hello: 'world' };
`,
},
run: {
stdout: `{"default":{"hello":"world"}}`,
},
});
itBundled("edgecase/EsmWrapperClassHoisting", {
files: {
"/entry.ts": `
async function hi() {
const { default: MyInherited } = await import('./hello');
const myInstance = new MyInherited();
console.log(myInstance.greet())
}
hi();
`,
"/hello.ts": `
const MyReassignedSuper = class MySuper {
greet() {
return 'Hello, world!';
}
};
class MyInherited extends MyReassignedSuper {};
export default MyInherited;
`,
},
run: {
stdout: "Hello, world!",
},
});
itBundled("edgecase/EsmWrapperElimination1", {
files: {
"/entry.ts": `
async function load() {
return import('./hello');
}
load().then(({ default: def }) => console.log(def()));
`,
"/hello.ts": `
export var x = 123;
export var y = function() { return x; };
export function z() { return y(); }
function a() { return z(); }
export default function c() { return a(); }
`,
},
run: {
stdout: "123",
},
});
itBundled("edgecase/TsEnumTreeShakingUseAndInlineClass", {
files: {
"/entry.ts": `
import { TestEnum } from './enum';
class TestClass {
constructor() {
console.log(JSON.stringify(TestEnum));
}
testMethod(name: TestEnum) {
return name === TestEnum.A;
}
}
// This must use wrapper class
console.log(new TestClass());
// This must inline
console.log(TestClass.prototype.testMethod.toString().includes('TestEnum'));
`,
"/enum.ts": `
export enum TestEnum {
A,
B,
}
`,
},
dce: true,
run: {
stdout: `
{"0":"A","1":"B","A":0,"B":1}
TestClass {
testMethod: [Function: testMethod],
}
false
`,
},
});
// this test checks that visit order doesnt matter (inline then use, above is use then inline)
itBundled("edgecase/TsEnumTreeShakingUseAndInlineClass2", {
files: {
"/entry.ts": `
import { TestEnum } from './enum';
class TestClass {
testMethod(name: TestEnum) {
return name === TestEnum.A;
}
constructor() {
console.log(JSON.stringify(TestEnum));
}
}
// This must use wrapper class
console.log(new TestClass());
// This must inline
console.log(TestClass.prototype.testMethod.toString().includes('TestEnum'));
`,
"/enum.ts": `
export enum TestEnum {
A,
B,
}
`,
},
dce: true,
run: {
stdout: `
{"0":"A","1":"B","A":0,"B":1}
TestClass {
testMethod: [Function: testMethod],
}
false
`,
},
});
itBundled("edgecase/TsEnumTreeShakingUseAndInlineNamespace", {
files: {
"/entry.ts": `
import { TestEnum } from './enum';
namespace TestClass {
console.log(JSON.stringify(TestEnum));
console.log((() => TestEnum.A).toString().includes('TestEnum'));
}
`,
"/enum.ts": `
export enum TestEnum {
A,
B,
}
`,
},
dce: true,
run: {
stdout: `
{"0":"A","1":"B","A":0,"B":1}
false
`,
},
});
itBundled("edgecase/ImportMetaMain", {
files: {
"/entry.ts": /* js */ `
import {other} from './other';
console.log(capture(import.meta.main), capture(require.main === module), ...other);
`,
"/other.ts": `
globalThis['ca' + 'pture'] = x => x;
export const other = [capture(require.main === module), capture(import.meta.main)];
`,
},
capture: ["false", "false", "import.meta.main", "import.meta.main"],
onAfterBundle(api) {
// This should not be marked as a CommonJS module
api.expectFile("/out.js").not.toContain("require");
api.expectFile("/out.js").not.toContain("module");
},
});
itBundled("edgecase/ImportMetaMainTargetNode", {
files: {
"/entry.ts": /* js */ `
import {other} from './other';
console.log(capture(import.meta.main), capture(require.main === module), ...other);
`,
"/other.ts": `
globalThis['ca' + 'pture'] = x => x;
export const other = [capture(require.main === module), capture(import.meta.main)];
`,
},
target: "node",
capture: ["false", "false", "__require.main == __require.module", "__require.main == __require.module"],
onAfterBundle(api) {
// This should not be marked as a CommonJS module
api.expectFile("/out.js").not.toMatch(/\brequire\b/); // __require is ok
api.expectFile("/out.js").not.toMatch(/[^\.:]module/); // `.module` and `node:module` are ok.
},
});
itBundled("edgecase/build-cjs-module#20308", {
files: {
"/entry.ts": /* js */ `
import {other} from './other';
console.log(capture(import.meta.main), capture(require.main === module), ...other);
`,
"/other.ts": /* js */ `
globalThis['ca' + 'pture'] = x => x;
export const other = [capture(require.main === module), capture(import.meta.main)];
`,
},
target: "node",
format: "cjs",
capture: ["false", "false", "require.main == module", "require.main == module"],
onAfterBundle(api) {
console.log(api.readFile("/out.js"));
// This should be marked as a CommonJS module
api.expectFile("/out.js").toMatch(/\brequire\b/); // __require is not ok
api.expectFile("/out.js").toMatch(/[^\.:]module/); // `.module` and `node:module` are not ok.
},
});
itBundled("edgecase/IdentifierInEnum#13081", {
files: {
"/entry.ts": `
let ZZZZZZZZZ = 1;
enum B {
C = ZZZZZZZZZ,
}
console.log(B.C);
`,
},
run: { stdout: "1" },
});
itBundled("edgecase/DoNotMoveTaggedTemplateLiterals", {
files: {
"/entry.ts": `
globalThis.z = () => console.log(2)
const y = await import('./second.ts');
`,
"/second.ts": `
console.log(1);
export const y = z\`zyx\`;
`,
},
run: { stdout: "1\n2" },
});
itBundled("edgecase/Latin1StringInImportedJSON", {
files: {
"/entry.ts": `
import x from './second.json';
console.log(x + 'a');
`,
"/second.json": `
"测试"
`,
},
target: "bun",
run: { stdout: `测试a` },
});
itBundled("edgecase/Latin1StringInImportedJSONBrowser", {
files: {
"/entry.ts": `
import x from './second.json';
console.log(x + 'a');
`,
"/second.json": `
"测试"
`,
},
target: "browser",
run: { stdout: `测试a` },
});
itBundled("edgecase/Latin1StringKey", {
files: {
"/entry.ts": `
import x from './second.json';
console.log(x["测试" + "a"]);
`,
"/second.json": `
{"测试a" : 123}
`,
},
target: "bun",
run: { stdout: `123` },
});
itBundled("edgecase/Latin1StringKeyBrowser", {
files: {
"/entry.ts": `
import x from './second.json';
console.log(x["测试" + "a"]);
`,
"/second.json": `
{"测试a" : 123}
`,
},
target: "browser",
run: { stdout: `123` },
});
itBundled("edgecase/UninitializedVariablesMoved", {
files: {
"/entry.ts": `
await import('./b.js');
`,
"/b.js": `
export var a = 32;
export var b;
(function (c) {
c.d = 1;
})(b ?? {});
+a;
`,
},
minifySyntax: true,
run: true, // pass if no thrown error
});
itBundled("edgecase/UsingExportDefault", {
files: {
"/entry.ts": `
import module from "./module.ts";
console.log(module.x);
`,
"/module.ts": `
using a = {
[Symbol.dispose]: () => {
console.log("Disposing");
}
};
export default {x: 1};
`,
},
run: {
stdout: "Disposing\n1",
},
});
itBundled("edgecase/UsingExportClass", {
files: {
"/entry.ts": `
export class A {
[Symbol.dispose](){
console.info("Disposing");
}
}
using a = new A();
`,
},
run: {
stdout: "Disposing",
},
});
itBundled("edgecase/UsingExportDefaultThrows", {
files: {
"/entry.ts": `
import("./module.ts").catch(error => {
console.log("Caught error:", error.message);
});
`,
"/module.ts": `
function somethingThatThrows() {
throw new Error("This function throws");
}
using disposable = {
[Symbol.dispose]: () => {
console.log("Disposing");
}
};
export default somethingThatThrows();
`,
},
run: {
stdout: "Disposing\nCaught error: This function throws",
},
});
itBundled("edgecase/UsingExportDefaultAsync", {
files: {
"/entry.ts": `
const result = await import("./importer.ts");
console.log(await result.default);
`,
"/importer.ts": `
async function main() {
using disposable = {
[Symbol.dispose]: () => {
console.log("Disposing");
}
};
return "Success";
}
export default main();
`,
},
run: {
stdout: "Disposing\nSuccess",
},
});
itBundled("edgecase/UsingDisposeThrowDoesntMask", {
files: {
"/entry.ts": `
using a = {
[Symbol.dispose]: () => {
throw new Error("Error");
}
};
using b = {
[Symbol.dispose]: () => {
console.log("Disposing");
}
}
`,
},
run: {
error: "error: Error",
stdout: "Disposing",
},
});
itBundled("edgecase/UsingExportFails", {
files: {
"/entry.ts": `
import a from "./import.ts";
console.log(a.ok);
`,
"/import.ts": `
using a = {
[Symbol.dispose]: () => {
console.log("Disposing");
},
ok: true,
};
export default a;
`,
},
run: {
stdout: "Disposing\ntrue",
},
});
itBundled("edgecase/NoOutWithTwoFiles", {
files: {
"/entry.ts": `
import index from './index.html' with { type: 'file' }
console.log(index);
`,
"/index.html": `
<head></head>
`,
},
generateOutput: false,
backend: "api",
onAfterApiBundle: async build => {
expect(build.success).toEqual(true);
expect(build.outputs).toBeArrayOfSize(2);
expect(build.outputs[0].path).toEqual("./entry.js");
expect(build.outputs[0].loader).toEqual("ts");
expect(build.outputs[0].kind).toEqual("entry-point");
expect(build.outputs[1].loader).toEqual("file");
expect(build.outputs[1].kind).toEqual("asset");
expect(await build.outputs[1].text()).toEqual("<head></head>");
},
});
itBundled("edgecase/OutWithTwoFiles", {
backend: "cli",
files: {
"/entry.ts": `
import index from './index.html' with { type: 'file' }
console.log(index);
`,
"/index.html": `
<head></head>
`,
},
generateOutput: true,
bundleErrors: {
"<bun>": ["cannot write multiple output files without an output directory"],
},
run: true,
});
// TODO(@paperclover): test every case of this. I had already tested it manually, but it may break later
const requireTranspilationListESM = [
// input, output:bun, output:node
["require", "import.meta.require", "__require"],
["typeof require", "import.meta.require", "typeof __require"],
["typeof require", "import.meta.require", "typeof __require"],
];
// // itBundled('edgecase/RequireTranspilation')
itBundled("edgecase/TSConfigPathsConfigDir", {
files: {
"/src/entry.ts": /* ts */ `
import { value } from "alias/foo";
import { other } from "@scope/bar";
import { nested } from "deep/path";
import { absolute } from "abs/path";
console.log(value, other, nested, absolute);
`,
"/src/actual/foo.ts": `export const value = "foo";`,
"/src/lib/bar.ts": `export const other = "bar";`,
"/src/nested/deep/file.ts": `export const nested = "nested";`,
"/src/absolute.ts": `export const absolute = "absolute";`,
"/src/tsconfig.json": /* json */ `{
"compilerOptions": {
"baseUrl": "\${configDir}",
"paths": {
"alias/*": ["actual/*"],
"@scope/*": ["lib/*"],
"deep/path": ["nested/deep/file.ts"],
"abs/*": ["\${configDir}/absolute.ts"]
}
}
}`,
},
run: {
stdout: "foo bar nested absolute",
},
});
itBundled("edgecase/TSConfigBaseUrlConfigDir", {
files: {
"/entry.ts": /* ts */ `
import { value } from "./src/subdir/module";
console.log(value);
`,
"/src/lib/module.ts": `export const value = "found";`,
"/src/subdir/module.ts": `
import { value } from "absolute";
export { value };
`,
"tsconfig.json": /* json */ `{
"compilerOptions": {
"baseUrl": "\${configDir}/src/lib",
"paths": {
"absolute": ["./module.ts"]
}
}
}`,
},
run: {
stdout: "found",
},
});
itBundled("edgecase/TSConfigPathsConfigDirWildcard", {
files: {
"/src/entry.ts": /* ts */ `
import { one } from "prefix/one";
import { two } from "prefix/two";
import { three } from "other/three";
console.log(one, two, three);
`,
"/src/modules/one.ts": `export const one = "one";`,
"/src/modules/two.ts": `export const two = "two";`,
"/src/alternate/three.ts": `export const three = "three";`,
"/src/tsconfig.json": /* json */ `{
"compilerOptions": {
"baseUrl": "\${configDir}",
"paths": {
"prefix/*": ["modules/*"],
"other/*": ["\${configDir}/alternate/*"]
}
}
}`,
},
run: {
stdout: "one two three",
},
});
itBundled("edgecase/TSConfigPathsConfigDirNested", {
files: {
"/deeply/nested/src/entry.ts": /* ts */ `
import { value } from "alias/module";
console.log(value);
`,
"/deeply/nested/src/actual/module.ts": `export const value = "nested";`,
"/deeply/nested/src/tsconfig.json": /* json */ `{
"compilerOptions": {
"baseUrl": "\${configDir}",
"paths": {
"alias/*": ["actual/*"]
}
}
}`,
},
run: {
stdout: "nested",
},
});
itBundled("edgecase/TSConfigPathsConfigDirMultiple", {
files: {
"/src/entry.ts": /* ts */ `
import { value } from "multi/module";
console.log(value);
`,
"/src/fallback/module.ts": `export const value = "fallback";`,
"/src/primary/module.ts": `export const value = "primary";`,
"/src/tsconfig.json": /* json */ `{
"compilerOptions": {
"baseUrl": "\${configDir}",
"paths": {
"multi/*": [
"\${configDir}/primary/*",
"\${configDir}/fallback/*"
]
}
}
}`,
},
run: {
stdout: "primary",
},
});
itBundled("edgecase/TSConfigPathsConfigDirInvalid", {
files: {
"/entry.ts": /* ts */ `
import { value } from "invalid/module";
console.log(value);
`,
"/tsconfig.json": /* json */ `{
"compilerOptions": {
"baseUrl": "\${configDir}",
"paths": {
"invalid/*": ["\${configDir}/\${configDir}/*"]
}
}
}`,
},
bundleErrors: {
"/entry.ts": ['Could not resolve: "invalid/module". Maybe you need to "bun install"?'],
},
});
itBundled("edgecase/TSConfigPathsConfigDirBackslash", {
files: {
"/entry.ts": /* ts */ `
import { value } from "windows/style";
console.log(value);
`,
"/win/style.ts": `export const value = "windows";`,
"/tsconfig.json": /* json */ `{
"compilerOptions": {
"baseUrl": "\${configDir}",
"paths": {
"windows/*": ["win\\\\*"]
}
}
}`,
},
run: {
stdout: "windows",
},
});
itBundled("edgecase/TSPublicFieldMinification", {
files: {
"/entry.ts": /* ts */ `
export class Foo {
constructor(public name: string) {}
}
const keys = Object.keys(new Foo('test'))
if (keys.length !== 1) throw new Error('Keys length is not 1')
if (keys[0] !== 'name') throw new Error('keys[0] is not "name"')
console.log('success')
`,
},
minifySyntax: true,
minifyIdentifiers: true,
target: "bun",
run: {
stdout: "success",
},
});
// https://github.com/oven-sh/bun/issues/14585
itBundled("identifiers/SameNameDifferentModulesWithMinifyIdentifiersDisabled", {
files: {
"/foo.js": `
{
var d = 0;
}
export const foo = () => {}
`,
"/bar.js": `
// bar.js - The collision happens with this function declaration
function d() {}
export function bar() {d.length;}
`,
"/index.js": `
import { foo } from "./foo.js";
import { bar } from "./bar.js";
// Execute in order
foo();
bar();
`,
},
entryPoints: ["/index.js"],
outfile: "/out.js",
minifyIdentifiers: false,
run: {
stdout: "",
},
});
itBundled("edgecase/NodeBuiltinWithoutPrefix", {
files: {
"/entry.ts": `
import * as hello from "node:test";
import * as world from "node:fs";
import * as etc from "console";
import * as blah from "bun:jsc";
+[hello,world,etc,blah];
`,
},
target: "bun",
onAfterBundle(api) {
api.expectFile("out.js").toMatchInlineSnapshot(`
"// @bun
// entry.ts
import * as hello from "node:test";
import * as world from "fs";
import * as etc from "console";
import * as blah from "bun:jsc";
+[hello, world, etc, blah];
"
`);
},
});
itBundled("edgecase/NodeBuiltinWithoutPrefix2", {
files: {
"/entry.ts": `
import * as hello from "node:test";
import * as world from "node:fs";
import * as etc from "console";
+[hello,world,etc];
`,
},
target: "node",
onAfterBundle(api) {
api.expectFile("out.js").toMatchInlineSnapshot(`
"// entry.ts
import * as hello from "node:test";
import * as world from "node:fs";
import * as etc from "console";
+[hello, world, etc];
"
`);
},
});
});
for (const backend of ["api", "cli"] as const) {
describe(`bundler_edgecase/${backend}`, () => {
itBundled("edgecase/ProcessEnvArbitrary", {
files: {
"/entry.js": /* js */ `
capture(process.env.ARBITRARY);
`,
},
target: "browser",
backend,
capture: ["process.env.ARBITRARY"],
env: {
ARBITRARY: "secret environment stuff!",
},
});
});
}