mirror of
https://github.com/oven-sh/bun
synced 2026-02-10 10:58:56 +00:00
I haven't checked all uses of tryTakeException but this bug is probably not the only one. Caught by running fuzzy-wuzzy with debug logging enabled. It tried to print the exception. Updates fuzzy-wuzzy to have improved logging that can tell you what was last executed before a crash. --------- Co-authored-by: Jarred Sumner <jarred@jarredsumner.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
447 lines
14 KiB
TypeScript
447 lines
14 KiB
TypeScript
import { pathToFileURL } from "bun";
|
||
import { describe, expect, it } from "bun:test";
|
||
import { mkdirSync, writeFileSync } from "fs";
|
||
import { bunEnv, bunExe, bunRun, isWindows, joinP, tempDirWithFiles } from "harness";
|
||
import { join, resolve, sep } from "path";
|
||
|
||
const fixture = (...segs: string[]) => resolve(import.meta.dir, "fixtures", ...segs);
|
||
|
||
it("spawn test file", () => {
|
||
writePackageJSONImportsFixture();
|
||
writePackageJSONExportsFixture();
|
||
|
||
const { exitCode } = Bun.spawnSync({
|
||
cmd: [bunExe(), "test", "./resolve-test.js"],
|
||
env: bunEnv,
|
||
cwd: import.meta.dir,
|
||
stdio: ["inherit", "inherit", "inherit"],
|
||
});
|
||
|
||
expect(exitCode).toBe(0);
|
||
});
|
||
|
||
function writePackageJSONExportsFixture() {
|
||
try {
|
||
mkdirSync(join(import.meta.dir, "./node_modules/package-json-exports/foo"), {
|
||
recursive: true,
|
||
});
|
||
} catch (exception) {}
|
||
writeFileSync(join(import.meta.dir, "./node_modules/package-json-exports/foo/bar.js"), "export const bar = 1;");
|
||
writeFileSync(
|
||
join(import.meta.dir, "./node_modules/package-json-exports/foo/references-baz.js"),
|
||
"export {bar} from 'package-json-exports/baz';",
|
||
);
|
||
writeFileSync(
|
||
join(import.meta.dir, "./node_modules/package-json-exports/package.json"),
|
||
JSON.stringify(
|
||
{
|
||
name: "package-json-exports",
|
||
exports: {
|
||
"./baz": "./foo/bar.js",
|
||
"./references-baz": "./foo/references-baz.js",
|
||
},
|
||
},
|
||
null,
|
||
2,
|
||
),
|
||
);
|
||
}
|
||
|
||
function writePackageJSONImportsFixture() {
|
||
try {
|
||
mkdirSync(join(import.meta.dir, "./node_modules/package-json-imports/foo"), {
|
||
recursive: true,
|
||
});
|
||
} catch (exception) {}
|
||
writeFileSync(join(import.meta.dir, "./node_modules/package-json-imports/foo/bar.js"), "export const bar = 1;");
|
||
writeFileSync(
|
||
join(import.meta.dir, "./node_modules/package-json-imports/foo/wildcard.js"),
|
||
"export const wildcard = 1;",
|
||
);
|
||
writeFileSync(
|
||
join(import.meta.dir, "./node_modules/package-json-imports/foo/private-foo.js"),
|
||
"export {bar} from 'package-json-imports/#foo';",
|
||
);
|
||
writeFileSync(
|
||
join(import.meta.dir, "./node_modules/package-json-imports/package.json"),
|
||
JSON.stringify(
|
||
{
|
||
name: "package-json-imports",
|
||
exports: {
|
||
"./baz": "./foo/bar.js",
|
||
},
|
||
imports: {
|
||
"#foo/bar": "./foo/private-foo.js",
|
||
"#foo/*.js": "./foo/*.js",
|
||
"#foo/extensionless/*": "./foo/*.js",
|
||
"#foo": "./foo/private-foo.js",
|
||
|
||
"#internal-react": "react",
|
||
|
||
"#to_node_module": "async_hooks",
|
||
},
|
||
},
|
||
null,
|
||
2,
|
||
),
|
||
);
|
||
}
|
||
|
||
it("file url in import resolves", async () => {
|
||
const dir = tempDirWithFiles("fileurl", {
|
||
"index.js": "export const foo = 1;",
|
||
});
|
||
writeFileSync(`${dir}/test.js`, `import {foo} from '${pathToFileURL(dir)}/index.js';\nconsole.log(foo);`);
|
||
|
||
console.log(dir);
|
||
const { exitCode, stdout } = Bun.spawnSync({
|
||
cmd: [bunExe(), `${dir}/test.js`],
|
||
env: bunEnv,
|
||
cwd: import.meta.dir,
|
||
});
|
||
try {
|
||
expect(exitCode).toBe(0);
|
||
} catch (e) {
|
||
console.log(stdout.toString("utf8"));
|
||
throw e;
|
||
}
|
||
expect(stdout.toString("utf8")).toBe("1\n");
|
||
});
|
||
|
||
it("invalid file url in import throws error", async () => {
|
||
const dir = tempDirWithFiles("fileurl", {});
|
||
writeFileSync(`${dir}/test.js`, `import {foo} from 'file://\0invalid url';\nconsole.log(foo);`);
|
||
|
||
const { exitCode, stdout, stderr } = Bun.spawnSync({
|
||
cmd: [bunExe(), `${dir}/test.js`],
|
||
env: bunEnv,
|
||
cwd: import.meta.dir,
|
||
});
|
||
expect(exitCode).not.toBe(0);
|
||
expect(stderr.toString("utf8")).toContain("file://\0invalid url");
|
||
});
|
||
|
||
it("file url in await import resolves", async () => {
|
||
const dir = tempDirWithFiles("fileurl", {
|
||
"index.js": "export const foo = 1;",
|
||
});
|
||
writeFileSync(`${dir}/test.js`, `const {foo} = await import('${pathToFileURL(dir)}/index.js');\nconsole.log(foo);`);
|
||
|
||
const { exitCode, stdout } = Bun.spawnSync({
|
||
cmd: [bunExe(), `${dir}/test.js`],
|
||
env: bunEnv,
|
||
cwd: import.meta.dir,
|
||
});
|
||
expect(exitCode).toBe(0);
|
||
expect(stdout.toString("utf8")).toBe("1\n");
|
||
});
|
||
|
||
it("file url with special characters in await import resolves", async () => {
|
||
const filename = "🅱️ndex.js";
|
||
const dir = tempDirWithFiles("file url", {
|
||
[filename]: "export const foo = 1;",
|
||
});
|
||
console.log(dir);
|
||
writeFileSync(
|
||
`${dir}/test.js`,
|
||
`const {foo} = await import('${pathToFileURL(dir)}/${encodeURIComponent(filename)}');\nconsole.log(foo);`,
|
||
);
|
||
|
||
const { exitCode, stdout } = Bun.spawnSync({
|
||
cmd: [bunExe(), `${dir}/test.js`],
|
||
env: bunEnv,
|
||
cwd: import.meta.dir,
|
||
});
|
||
expect(exitCode).toBe(0);
|
||
expect(stdout.toString("utf8")).toBe("1\n");
|
||
});
|
||
|
||
it("file url with special characters not encoded in await import resolves", async () => {
|
||
const filename = "🅱️ndex.js";
|
||
const dir = tempDirWithFiles("file url", {
|
||
[filename]: "export const foo = 1;",
|
||
});
|
||
writeFileSync(
|
||
`${dir}/test.js`,
|
||
`const {foo} = await import('${pathToFileURL(dir)}/${filename}');\nconsole.log(foo);`,
|
||
);
|
||
|
||
const { exitCode, stdout } = Bun.spawnSync({
|
||
cmd: [bunExe(), `${dir}/test.js`],
|
||
env: bunEnv,
|
||
cwd: import.meta.dir,
|
||
});
|
||
expect(exitCode).toBe(0);
|
||
expect(stdout.toString("utf8")).toBe("1\n");
|
||
});
|
||
|
||
it("file url with special characters in import statement resolves", async () => {
|
||
const filename = "🅱️ndex.js";
|
||
const dir = tempDirWithFiles("file url", {
|
||
[filename]: "export const foo = 1;",
|
||
});
|
||
writeFileSync(
|
||
`${dir}/test.js`,
|
||
`import {foo} from '${pathToFileURL(dir)}/${encodeURIComponent(filename)}';\nconsole.log(foo);`,
|
||
);
|
||
|
||
const { exitCode, stdout } = Bun.spawnSync({
|
||
cmd: [bunExe(), `${dir}/test.js`],
|
||
env: bunEnv,
|
||
cwd: import.meta.dir,
|
||
});
|
||
expect(exitCode).toBe(0);
|
||
expect(stdout.toString("utf8")).toBe("1\n");
|
||
});
|
||
|
||
it("file url with special characters not encoded in import statement resolves", async () => {
|
||
const filename = "🅱️ndex.js";
|
||
const dir = tempDirWithFiles("file url", {
|
||
[filename]: "export const foo = 1;",
|
||
});
|
||
writeFileSync(`${dir}/test.js`, `import {foo} from '${pathToFileURL(dir)}/${filename}';\nconsole.log(foo);`);
|
||
|
||
const { exitCode, stdout } = Bun.spawnSync({
|
||
cmd: [bunExe(), `${dir}/test.js`],
|
||
env: bunEnv,
|
||
cwd: import.meta.dir,
|
||
});
|
||
expect(exitCode).toBe(0);
|
||
expect(stdout.toString("utf8")).toBe("1\n");
|
||
});
|
||
|
||
it("file url in require resolves", async () => {
|
||
const dir = tempDirWithFiles("fileurl", {
|
||
"index.js": "export const foo = 1;",
|
||
});
|
||
writeFileSync(`${dir}/test.js`, `const {foo} = require('${pathToFileURL(dir)}/index.js');\nconsole.log(foo);`);
|
||
|
||
const { exitCode, stdout } = Bun.spawnSync({
|
||
cmd: [bunExe(), `${dir}/test.js`],
|
||
env: bunEnv,
|
||
cwd: import.meta.dir,
|
||
});
|
||
expect(exitCode).toBe(0);
|
||
expect(stdout.toString("utf8")).toBe("1\n");
|
||
});
|
||
|
||
it("file url with special characters in require resolves", async () => {
|
||
const filename = "🅱️ndex.js";
|
||
const dir = tempDirWithFiles("file url", {
|
||
[filename]: "export const foo = 1;",
|
||
});
|
||
writeFileSync(
|
||
`${dir}/test.js`,
|
||
`const {foo} = require('${pathToFileURL(dir)}/${encodeURIComponent(filename)}');\nconsole.log(foo);`,
|
||
);
|
||
|
||
const { exitCode, stdout } = Bun.spawnSync({
|
||
cmd: [bunExe(), `${dir}/test.js`],
|
||
env: bunEnv,
|
||
cwd: import.meta.dir,
|
||
});
|
||
expect(exitCode).toBe(0);
|
||
expect(stdout.toString("utf8")).toBe("1\n");
|
||
});
|
||
|
||
it("file url in require.resolve resolves", async () => {
|
||
const dir = tempDirWithFiles("fileurl", {
|
||
"index.js": "export const foo = 1;",
|
||
});
|
||
writeFileSync(`${dir}/test.js`, `const to = require.resolve('${pathToFileURL(dir)}/index.js');\nconsole.log(to);`);
|
||
|
||
const { exitCode, stdout } = Bun.spawnSync({
|
||
cmd: [bunExe(), `${dir}/test.js`],
|
||
env: bunEnv,
|
||
cwd: import.meta.dir,
|
||
});
|
||
expect(exitCode).toBe(0);
|
||
expect(stdout.toString("utf8")).toBe(`${dir}${sep}index.js\n`);
|
||
});
|
||
|
||
it("file url with special characters in require resolves", async () => {
|
||
const filename = "🅱️ndex.js";
|
||
const dir = tempDirWithFiles("file url", {
|
||
[filename]: "export const foo = 1;",
|
||
});
|
||
writeFileSync(
|
||
`${dir}/test.js`,
|
||
`const to = require.resolve('${pathToFileURL(dir)}/${encodeURIComponent(filename)}');\nconsole.log(to);`,
|
||
);
|
||
|
||
const { exitCode, stdout } = Bun.spawnSync({
|
||
cmd: [bunExe(), `${dir}/test.js`],
|
||
env: bunEnv,
|
||
cwd: import.meta.dir,
|
||
});
|
||
expect(exitCode).toBe(0);
|
||
expect(stdout.toString("utf8")).toBe(`${dir}${sep}${filename}\n`);
|
||
});
|
||
|
||
it("import long string should not segfault", async () => {
|
||
try {
|
||
await import("a".repeat(10000));
|
||
} catch {}
|
||
});
|
||
it("import long string should not segfault", async () => {
|
||
try {
|
||
import.meta.require("a".repeat(10000));
|
||
} catch {}
|
||
});
|
||
it("import long string should not segfault", async () => {
|
||
try {
|
||
await import.meta.resolve!("a".repeat(10000));
|
||
} catch {}
|
||
});
|
||
it("import long string should not segfault", async () => {
|
||
try {
|
||
await import.meta.require.resolve("a".repeat(10000));
|
||
} catch {}
|
||
});
|
||
|
||
it("import override to node builtin", async () => {
|
||
// @ts-expect-error
|
||
expect(await import("#async_hooks")).toBeDefined();
|
||
});
|
||
|
||
it("import override to bun", async () => {
|
||
// @ts-expect-error
|
||
expect(await import("#bun")).toBeDefined();
|
||
});
|
||
|
||
it.todo("import override to bun:test", async () => {
|
||
// @ts-expect-error
|
||
expect(await import("#bun_test")).toBeDefined();
|
||
});
|
||
|
||
it.if(isWindows)("directory cache key computation", () => {
|
||
expect(import(`${process.cwd()}\\\\doesnotexist.ts`)).rejects.toThrow();
|
||
expect(import(`${process.cwd()}\\\\\\doesnotexist.ts`)).rejects.toThrow();
|
||
expect(import(`\\\\Test\\\\doesnotexist.ts\\` as any)).rejects.toThrow();
|
||
expect(import(`\\\\Test\\\\doesnotexist.ts\\\\` as any)).rejects.toThrow();
|
||
expect(import(`\\\\Test\\\\doesnotexist.ts\\\\\\` as any)).rejects.toThrow();
|
||
expect(import(`\\\\Test\\\\\\doesnotexist.ts` as any)).rejects.toThrow();
|
||
expect(import(`\\\\Test\\\\\\\\doesnotexist.ts` as any)).rejects.toThrow();
|
||
expect(import(`\\\\Test\\doesnotexist.ts` as any)).rejects.toThrow();
|
||
expect(import(`\\\\\\Test\\doesnotexist.ts` as any)).rejects.toThrow();
|
||
expect(import(`\\\\Test\\\\\\doesnotexist.ts\\` as any)).rejects.toThrow();
|
||
expect(import(`\\\\Test\\\\\\\\doesnotexist.ts\\` as any)).rejects.toThrow();
|
||
expect(import(`\\\\Test\\doesnotexist.ts\\` as any)).rejects.toThrow();
|
||
expect(import(`\\\\\\Test\\doesnotexist.ts\\` as any)).rejects.toThrow();
|
||
expect(import(`\\\\Test\\\\\\doesnotexist.ts\\\\` as any)).rejects.toThrow();
|
||
expect(import(`\\\\Test\\\\\\\\doesnotexist.ts\\\\` as any)).rejects.toThrow();
|
||
expect(import(`\\\\Test\\doesnotexist.ts\\\\` as any)).rejects.toThrow();
|
||
expect(import(`\\\\\\Test\\doesnotexist.ts\\\\` as any)).rejects.toThrow();
|
||
});
|
||
|
||
describe("NODE_PATH test", () => {
|
||
const prepareTest = () => {
|
||
const tempDir = tempDirWithFiles("node_path", {
|
||
"modules/node_modules/node-path-test/index.js": "exports.testValue = 'NODE_PATH works';",
|
||
"modules/node_modules/node-path-test/package.json": JSON.stringify({
|
||
name: "node-path-test",
|
||
version: "1.0.0",
|
||
description: "A node_path test module",
|
||
main: "index.js",
|
||
}),
|
||
"lib/node_modules/node-path-test/index.js": "exports.testValue = 'NODE_PATH from lib works';",
|
||
"lib/node_modules/node-path-test/package.json": JSON.stringify({
|
||
name: "node-path-test",
|
||
version: "1.0.0",
|
||
description: "A node_path test module from lib",
|
||
main: "index.js",
|
||
}),
|
||
"test/index.js": "const { testValue } = require('node-path-test');\nconsole.log(testValue);",
|
||
});
|
||
|
||
const nodePath = joinP(tempDir, "modules/node_modules");
|
||
const nodePathLib = joinP(tempDir, "lib/node_modules");
|
||
const testDir = joinP(tempDir, "test");
|
||
|
||
const delimiter = isWindows ? ";" : ":";
|
||
|
||
return {
|
||
tempDir,
|
||
nodePath,
|
||
nodePathLib,
|
||
testDir,
|
||
delimiter,
|
||
};
|
||
};
|
||
|
||
it("should resolve modules from NODE_PATH", () => {
|
||
const { nodePath, testDir } = prepareTest();
|
||
|
||
const { exitCode, stdout } = Bun.spawnSync({
|
||
cmd: [bunExe(), "--no-install", "index.js"],
|
||
env: { ...bunEnv, NODE_PATH: nodePath },
|
||
cwd: testDir,
|
||
});
|
||
|
||
expect(exitCode).toBe(0);
|
||
expect(stdout.toString().trim()).toBe("NODE_PATH works");
|
||
});
|
||
|
||
it("should resolve modules from NODE_PATH entries", () => {
|
||
const { nodePath, testDir, delimiter } = prepareTest();
|
||
|
||
const { exitCode, stdout } = Bun.spawnSync({
|
||
cmd: [bunExe(), "--no-install", "index.js"],
|
||
env: { ...bunEnv, NODE_PATH: [nodePath].join(delimiter) },
|
||
cwd: testDir,
|
||
});
|
||
|
||
expect(exitCode).toBe(0);
|
||
expect(stdout.toString().trim()).toBe("NODE_PATH works");
|
||
});
|
||
|
||
it("should resolve first matched module from NODE_PATH entries", () => {
|
||
const { nodePath, nodePathLib, testDir, delimiter } = prepareTest();
|
||
|
||
const { exitCode, stdout } = Bun.spawnSync({
|
||
cmd: [bunExe(), "--no-install", "index.js"],
|
||
env: { ...bunEnv, NODE_PATH: ["/a/path/not/exist", nodePathLib, nodePath].join(delimiter) },
|
||
cwd: testDir,
|
||
});
|
||
|
||
expect(exitCode).toBe(0);
|
||
expect(stdout.toString().trim()).toBe("NODE_PATH from lib works");
|
||
});
|
||
});
|
||
|
||
it("can resolve with source directories that do not exist", () => {
|
||
// In Nuxt/Vite, the following call happens:
|
||
// `require("module").createRequire("file:///Users/clo/my-nuxt-app/@vue/server-renderer")("vue")`
|
||
// This seems to be a bug in their code, not using a concrete file path for
|
||
// this virtual module, such as 'node_modules/@vue/server-renderer/index.js',
|
||
// but the same exact resolution happens and succeeds in Node.js
|
||
const dir = tempDirWithFiles("resolve", {
|
||
"node_modules/vue/index.js": "export default 123;",
|
||
"test.js": `
|
||
const { createRequire } = require('module');
|
||
const assert = require('assert');
|
||
const req = createRequire(import.meta.url + '/@vue/server-renderer');
|
||
assert.strictEqual(req('vue').default, 123);
|
||
`,
|
||
});
|
||
|
||
const { exitCode, stdout } = Bun.spawnSync({
|
||
cmd: [bunExe(), "test.js"],
|
||
env: bunEnv,
|
||
cwd: dir,
|
||
stdio: ["ignore", "inherit", "inherit"],
|
||
});
|
||
|
||
expect(exitCode).toBe(0);
|
||
});
|
||
|
||
describe("When CJS and ESM are mixed", () => {
|
||
const fixturePath = fixture("tsyringe.ts");
|
||
|
||
// https://github.com/oven-sh/bun/issues/4677
|
||
it("loads reflect-metadata before tsyringe", async () => {
|
||
const { stderr } = bunRun(fixturePath);
|
||
expect(stderr).toBeEmpty();
|
||
});
|
||
});
|