Compare commits

...

4 Commits

Author SHA1 Message Date
RiskyMH
e348a3199c deps: update libarchive to v3.8.5 (dd897a78c662a2c7a003e7ec158cea7909557bee) 2026-01-25 03:37:44 +00:00
robobun
70fe76209b fix(readline): use symbol key for _refreshLine in tab completer (#26412) 2026-01-24 15:37:29 -08:00
Jarred Sumner
ab3df344b8 Delete slop test 2026-01-23 23:22:32 -08:00
robobun
4680e89a91 fix(bundler): add missing semicolons in minified bun module imports (#26372)
## Summary
- Fix missing semicolons in minified output when using both default and
named imports from `"bun"` module
- The issue occurred in `printInternalBunImport` when transitioning
between star_name, default_name, and items sections without flushing
pending semicolons

## Test plan
- Added regression tests in `test/regression/issue/26371.test.ts`
covering:
  - Default + named imports (`import bun, { embeddedFiles } from "bun"`)
- Namespace + named imports (`import * as bun from "bun"; import {
embeddedFiles } from "bun"`)
  - Namespace + default + named imports combination
- Verified test fails with `USE_SYSTEM_BUN=1` (reproduces bug)
- Verified test passes with `bun bd test` (fix works)

Fixes #26371

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 23:09:01 -08:00
6 changed files with 104 additions and 91 deletions

View File

@@ -4,7 +4,7 @@ register_repository(
REPOSITORY
libarchive/libarchive
COMMIT
9525f90ca4bd14c7b335e2f8c84a4607b0af6bdf
dd897a78c662a2c7a003e7ec158cea7909557bee
)
register_cmake_command(

View File

@@ -1534,7 +1534,7 @@ var _Interface = class Interface extends InterfaceConstructor {
prefix +
StringPrototypeSlice.$call(this.line, this.cursor, this.line.length);
this.cursor = this.cursor - completeOn.length + prefix.length;
this._refreshLine();
this[kRefreshLine]();
return;
}

View File

@@ -964,6 +964,7 @@ fn NewPrinter(
}
if (import.default_name) |default| {
p.printSemicolonIfNeeded();
p.print("var ");
p.printSymbol(default.ref.?);
if (comptime Statement == void) {
@@ -984,6 +985,7 @@ fn NewPrinter(
}
if (import.items.len > 0) {
p.printSemicolonIfNeeded();
p.printWhitespacer(ws("var {"));
if (!import.is_single_line) {

View File

@@ -1109,15 +1109,15 @@ describe("bundler", () => {
"/entry.js": /* js */ `
// Test all equality operators with typeof undefined
console.log(typeof x !== 'undefined');
console.log(typeof x != 'undefined');
console.log(typeof x != 'undefined');
console.log('undefined' !== typeof x);
console.log('undefined' != typeof x);
console.log(typeof x === 'undefined');
console.log(typeof x == 'undefined');
console.log('undefined' === typeof x);
console.log('undefined' == typeof x);
// These should not be optimized
console.log(typeof x === 'string');
console.log(x === 'undefined');
@@ -1135,4 +1135,61 @@ describe("bundler", () => {
);
},
});
// https://github.com/oven-sh/bun/issues/26371
// Minified bundler output missing semicolon between statements when
// using both default and named imports from "bun" module
itBundled("minify/BunImportSemicolonInsertion", {
files: {
"/entry.js": /* js */ `
import bun, { embeddedFiles } from "bun"
console.log(typeof embeddedFiles)
console.log(typeof bun.argv)
`,
},
minifySyntax: true,
minifyWhitespace: true,
minifyIdentifiers: true,
target: "bun",
run: {
stdout: "object\nobject",
},
});
itBundled("minify/BunImportNamespaceAndNamed", {
files: {
"/entry.js": /* js */ `
import * as bun from "bun"
import { embeddedFiles } from "bun"
console.log(typeof embeddedFiles)
console.log(typeof bun.argv)
`,
},
minifySyntax: true,
minifyWhitespace: true,
minifyIdentifiers: true,
target: "bun",
run: {
stdout: "object\nobject",
},
});
itBundled("minify/BunImportDefaultNamespaceAndNamed", {
files: {
"/entry.js": /* js */ `
import bun, * as bunNs from "bun"
import { embeddedFiles } from "bun"
console.log(typeof embeddedFiles)
console.log(typeof bun.argv)
console.log(typeof bunNs.argv)
`,
},
minifySyntax: true,
minifyWhitespace: true,
minifyIdentifiers: true,
target: "bun",
run: {
stdout: "object\nobject\nobject",
},
});
});

View File

@@ -1,86 +0,0 @@
import { expect, test } from "bun:test";
// https://github.com/oven-sh/bun/issues/26394
// Race condition in Bun.serve() where requests can arrive before routes are fully registered,
// causing the default "Welcome to Bun!" response instead of the configured handler's response.
test("concurrent Bun.serve instances should not return Welcome to Bun", async () => {
const serverCount = 60;
const servers: ReturnType<typeof Bun.serve>[] = [];
try {
// Create many servers concurrently
for (let i = 0; i < serverCount; i++) {
servers.push(
Bun.serve({
port: 0,
fetch: () => new Response("OK"),
}),
);
}
// Make concurrent requests to all servers
const responses = await Promise.all(
servers.map(async server => {
const res = await fetch(`http://127.0.0.1:${server.port}/`);
return res.text();
}),
);
// Verify no "Welcome to Bun!" responses - check for both debug mode message and production mode
for (let i = 0; i < responses.length; i++) {
expect(responses[i]).not.toContain("Welcome to Bun");
expect(responses[i]).not.toBe(""); // Production mode returns empty for renderMissing
expect(responses[i]).toBe("OK");
}
} finally {
// Clean up - guaranteed to run even if assertions fail
for (const server of servers) {
server.stop();
}
}
});
test("Bun.serve should be ready to handle requests immediately after returning", async () => {
// Test a single server with immediate fetch - this tests if the server is ready synchronously
using server = Bun.serve({
port: 0,
fetch: () => new Response("handler response"),
});
// Immediately fetch - if there's a race condition, this might return "Welcome to Bun!"
const response = await fetch(`http://127.0.0.1:${server.port}/`);
const text = await response.text();
expect(text).toBe("handler response");
});
test("multiple sequential Bun.serve instances with immediate requests", async () => {
// Create servers sequentially and immediately request from each
const results: string[] = [];
const servers: ReturnType<typeof Bun.serve>[] = [];
try {
for (let i = 0; i < 20; i++) {
const server = Bun.serve({
port: 0,
fetch: () => new Response(`server-${i}`),
});
servers.push(server);
// Immediately fetch from the server
const response = await fetch(`http://127.0.0.1:${server.port}/`);
results.push(await response.text());
}
// Verify all responses match expected
for (let i = 0; i < results.length; i++) {
expect(results[i]).toBe(`server-${i}`);
}
} finally {
// Clean up - guaranteed to run even if assertions fail
for (const server of servers) {
server.stop();
}
}
});

View File

@@ -0,0 +1,40 @@
import { expect, test } from "bun:test";
import { bunEnv, bunExe } from "harness";
// https://github.com/oven-sh/bun/issues/26411
// Tab completion with node:readline/promises threw
// "TypeError: this._refreshLine is not a function"
test("tab completion works with node:readline/promises", async () => {
await using proc = Bun.spawn({
cmd: [
bunExe(),
"-e",
`
import readline from "node:readline/promises";
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: true,
completer: (line) => [["FOO", "FOOBAR"], line]
});
rl.line = "foo";
rl.cursor = 3;
setTimeout(() => {
rl.close();
console.log("OK");
process.exit(0);
}, 100);
rl.write("", { name: "tab" });
`,
],
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
expect(stderr).not.toContain("this._refreshLine is not a function");
expect(stdout).toContain("OK");
expect(exitCode).toBe(0);
});