Files
bun-src/test/cli/install/shebang-normalize.test.ts
2026-08-27 21:09:14 +00:00

104 lines
3.1 KiB
TypeScript

import { spawn } from "bun";
import { expect, test } from "bun:test";
import { mkdir, readFile, stat, writeFile } from "fs/promises";
import { bunExe, bunEnv as env, isWindows, runBunInstall, tempDir, tmpdirSync } from "harness";
import { join } from "path";
test.skipIf(isWindows)("bin linking normalizes CRLF in shebang", async () => {
const testDir = tmpdirSync();
const pkgDir = join(testDir, "pkg");
const consumerDir = join(testDir, "consumer");
await mkdir(pkgDir, { recursive: true });
await mkdir(consumerDir, { recursive: true });
// Create package with bin that has CRLF shebang
await writeFile(
join(pkgDir, "package.json"),
JSON.stringify({
name: "test-pkg-crlf",
version: "1.0.0",
bin: {
"test-bin": "test-bin.py",
},
}),
);
// Write bin file with CRLF shebang
await writeFile(join(pkgDir, "test-bin.py"), "#!/usr/bin/env python\r\nprint('hello from python')");
// Link the package
const linkResult = spawn({
cmd: [bunExe(), "link"],
cwd: pkgDir,
env,
stdout: "pipe",
stderr: "pipe",
});
await linkResult.exited;
expect(linkResult.exitCode).toBe(0);
// Create consumer package
await writeFile(
join(consumerDir, "package.json"),
JSON.stringify({
name: "consumer",
version: "1.0.0",
dependencies: {
"test-pkg-crlf": "link:test-pkg-crlf",
},
}),
);
// Install
await runBunInstall(env, consumerDir);
// Check that the linked bin file has normalized shebang
const binPath = join(consumerDir, "node_modules", "test-pkg-crlf", "test-bin.py");
const binContent = await readFile(binPath, "utf-8");
console.log("Bin content first 50 chars:", JSON.stringify(binContent.slice(0, 50)));
expect(binContent).toStartWith("#!/usr/bin/env python\nprint");
expect(binContent).not.toContain("\r\n");
// Verify that the file is executable (bin linking sets this)
const binStat = await stat(binPath);
expect(binStat.mode & 0o111).toBeGreaterThan(0); // At least one execute bit should be set
});
test.skipIf(!isWindows)("bin linking reports a shebang launcher that is not valid UTF-8", async () => {
using dir = tempDir("shebang-invalid-utf8", {
"pkg/package.json": JSON.stringify({
name: "shebang-pkg",
version: "1.0.0",
bin: { "shebang-bin": "shebang-bin.js" },
}),
"app/package.json": JSON.stringify({
name: "app",
version: "1.0.0",
dependencies: { "shebang-pkg": "file:../pkg" },
}),
});
await writeFile(
join(String(dir), "pkg", "shebang-bin.js"),
Buffer.concat([Buffer.from("#!/usr/bin/env n"), Buffer.from([0x80, 0x80]), Buffer.from("\nconsole.log(1)\n")]),
);
await using proc = spawn({
cmd: [bunExe(), "install"],
cwd: join(String(dir), "app"),
env,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
expect(stderr).toContain("Failed to link");
expect(stderr).toContain("InvalidBinContent");
expect(await Bun.file(join(String(dir), "app", "node_modules", "shebang-pkg", "package.json")).exists()).toBe(true);
expect(exitCode).toBe(1);
});