This commit is contained in:
Claude Bot
2025-08-28 08:11:43 +00:00
parent 3545cca8cc
commit 82d3932961
2 changed files with 188 additions and 22 deletions

View File

@@ -404,32 +404,45 @@ export function runOnResolvePlugins(this: BundlerPlugin, specifier, inputNamespa
for (let [filter, callback] of results) {
if (filter.test(inputPath)) {
var result = callback({
path: inputPath,
importer,
namespace: inputNamespace,
resolveDir: inputNamespace === "file" ? require("node:path").dirname(importer) : undefined,
kind,
// pluginData
});
var result;
try {
result = callback({
path: inputPath,
importer,
namespace: inputNamespace,
resolveDir: inputNamespace === "file" ? require("node:path").dirname(importer) : undefined,
kind,
// pluginData
});
while (
result &&
$isPromise(result) &&
($getPromiseInternalField(result, $promiseFieldFlags) & $promiseStateMask) === $promiseStateFulfilled
) {
result = $getPromiseInternalField(result, $promiseFieldReactionsOrResult);
}
if (result && $isPromise(result)) {
result = await result;
}
if (!result || !$isObject(result)) {
// Handle promise resolution carefully with explicit undefined handling
if ($isPromise(result)) {
try {
const promiseResult = await result;
// Explicitly handle undefined/null promise results
if (promiseResult === undefined || promiseResult === null) {
continue;
}
result = promiseResult;
} catch (e) {
// If promise rejection, continue to next plugin
continue;
}
}
} catch (e) {
// If callback throws or causes internal error, continue to next plugin
continue;
}
var { path, namespace: userNamespace = inputNamespace, external } = result;
// Explicitly handle undefined return values from plugins
if (result === undefined || result === null || !$isObject(result)) {
continue;
}
// Safely extract properties with explicit undefined checks
var path = result.path;
var userNamespace = result.namespace !== undefined ? result.namespace : inputNamespace;
var external = result.external;
if (path !== undefined && typeof path !== "string") {
throw new TypeError("onResolve plugins 'path' field must be a string if provided");
}

View File

@@ -0,0 +1,153 @@
import { describe, test, expect } from "bun:test";
import { bunEnv, bunExe, tempDirWithFiles } from "harness";
// Regression test for issue #22199: segmentation fault when build.onResolve returns undefined
describe("issue #22199", () => {
test("onResolve returning undefined should not crash - sync", async () => {
const tempDir = tempDirWithFiles("plugin-test", {
"plugin.ts": /* ts */ `
Bun.plugin({
name: "test-plugin",
setup(build) {
build.onResolve({ filter: /.*\.(ts|tsx|js|jsx)$/ }, (args) => {
return undefined; // This should not crash
});
},
});
`,
"index.ts": /* ts */ `
console.log("Hello, World");
`,
});
await using proc = Bun.spawn({
cmd: [bunExe(), "--preload", "plugin.ts", "index.ts"],
cwd: tempDir,
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([
proc.stdout.text(),
proc.stderr.text(),
proc.exited,
]);
// Should not crash with segmentation fault
expect(exitCode).toBe(0);
expect(stdout.trim()).toBe("Hello, World");
expect(stderr).toBe("");
});
test("onResolve returning undefined should not crash - async", async () => {
const tempDir = tempDirWithFiles("plugin-test-async", {
"plugin.ts": /* ts */ `
Bun.plugin({
name: "test-plugin",
setup(build) {
build.onResolve({ filter: /.*\.(ts|tsx|js|jsx)$/ }, async (args) => {
return undefined; // This should not crash
});
},
});
`,
"index.ts": /* ts */ `
console.log("Hello, World");
`,
});
await using proc = Bun.spawn({
cmd: [bunExe(), "--preload", "plugin.ts", "index.ts"],
cwd: tempDir,
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([
proc.stdout.text(),
proc.stderr.text(),
proc.exited,
]);
// Should not crash with segmentation fault
expect(exitCode).toBe(0);
expect(stdout.trim()).toBe("Hello, World");
expect(stderr).toBe("");
});
test("onResolve returning Promise.resolve(undefined) should not crash", async () => {
const tempDir = tempDirWithFiles("plugin-test-promise", {
"plugin.ts": /* ts */ `
Bun.plugin({
name: "test-plugin",
setup(build) {
build.onResolve({ filter: /.*\.(ts|tsx|js|jsx)$/ }, (args) => {
return Promise.resolve(undefined); // This should not crash
});
},
});
`,
"index.ts": /* ts */ `
console.log("Hello, World");
`,
});
await using proc = Bun.spawn({
cmd: [bunExe(), "--preload", "plugin.ts", "index.ts"],
cwd: tempDir,
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([
proc.stdout.text(),
proc.stderr.text(),
proc.exited,
]);
// Should not crash with segmentation fault
expect(exitCode).toBe(0);
expect(stdout.trim()).toBe("Hello, World");
expect(stderr).toBe("");
});
test("onResolve returning null should not crash - async", async () => {
const tempDir = tempDirWithFiles("plugin-test-null", {
"plugin.ts": /* ts */ `
Bun.plugin({
name: "test-plugin",
setup(build) {
build.onResolve({ filter: /.*\.(ts|tsx|js|jsx)$/ }, async (args) => {
return null; // This should not crash
});
},
});
`,
"index.ts": /* ts */ `
console.log("Hello, World");
`,
});
await using proc = Bun.spawn({
cmd: [bunExe(), "--preload", "plugin.ts", "index.ts"],
cwd: tempDir,
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([
proc.stdout.text(),
proc.stderr.text(),
proc.exited,
]);
// Should not crash with segmentation fault
expect(exitCode).toBe(0);
expect(stdout.trim()).toBe("Hello, World");
expect(stderr).toBe("");
});
});