Files
bun.sh/packages/bun-release/src/fs.ts
2025-05-12 17:12:17 -07:00

160 lines
3.9 KiB
TypeScript

import crypto from "crypto";
import fs from "fs";
import os from "os";
import path from "path";
import { debug } from "./console";
export function join(...paths: (string | string[])[]): string {
return path.join(...paths.flat(2));
}
export function basename(...paths: (string | string[])[]): string {
return path.basename(join(...paths));
}
export function tmp(): string {
const tmpdir = process.env["RUNNER_TEMP"] ?? os.tmpdir();
const dir = fs.mkdtempSync(join(tmpdir, "bun-"));
debug("tmp", dir);
return dir;
}
export function rm(path: string): void {
debug("rm", path);
try {
fs.rmSync(path, { recursive: true });
return;
} catch (error) {
debug("fs.rmSync failed", error);
// Did not exist before Node.js v14.
// Attempt again with older, slower implementation.
}
let stats: fs.Stats;
try {
stats = fs.lstatSync(path);
} catch (error) {
debug("fs.lstatSync failed", error);
// The file was likely deleted, so return early.
return;
}
if (!stats.isDirectory()) {
fs.unlinkSync(path);
return;
}
try {
fs.rmdirSync(path, { recursive: true });
return;
} catch (error) {
debug("fs.rmdirSync failed", error);
// Recursive flag did not exist before Node.js X.
// Attempt again with older, slower implementation.
}
for (const filename of fs.readdirSync(path)) {
rm(join(path, filename));
}
fs.rmdirSync(path);
}
export function rename(path: string, newPath: string): void {
debug("rename", path, newPath);
try {
fs.renameSync(path, newPath);
return;
} catch (error) {
debug("fs.renameSync failed", error);
// If there is an error, delete the new path and try again.
}
try {
rm(newPath);
} catch (error) {
debug("rm failed", error);
// The path could have been deleted already.
}
fs.renameSync(path, newPath);
}
export function write(dst: string, content: string | ArrayBuffer | ArrayBufferView): void {
debug("write", dst);
try {
fs.writeFileSync(dst, content);
return;
} catch (error) {
debug("fs.writeFileSync failed", error);
// If there is an error, ensure the parent directory
// exists and try again.
try {
fs.mkdirSync(path.dirname(dst), { recursive: true });
} catch (error) {
debug("fs.mkdirSync failed", error);
// The directory could have been created already.
}
fs.writeFileSync(dst, content);
}
}
export function writeJson(path: string, json: object, force?: boolean): void {
let value = json;
if (!force && exists(path)) {
try {
const existing = JSON.parse(read(path));
value = {
...existing,
...json,
};
} catch {
value = json;
}
}
write(path, `${JSON.stringify(value, undefined, 2)}\n`);
}
export function read(path: string): string {
debug("read", path);
return fs.readFileSync(path, "utf-8");
}
export function blob(path: string): Blob {
debug("blob", path);
if ("Bun" in globalThis) {
return Bun.file(path);
}
const buffer = fs.readFileSync(path);
return new Blob([buffer], {
type: path.endsWith(".zip") ? "application/zip" : path.endsWith(".txt") ? "text/plain" : "application/octet-stream",
});
}
export function hash(content: string | crypto.BinaryLike): string {
debug("hash", content);
return crypto
.createHash("sha256")
.update(typeof content === "string" ? fs.readFileSync(content) : content)
.digest("hex");
}
export function chmod(path: string, mode: fs.Mode): void {
debug("chmod", path, mode);
fs.chmodSync(path, mode);
}
export function copy(path: string, newPath: string): void {
debug("copy", path, newPath);
try {
fs.copyFileSync(path, newPath);
return;
} catch (error) {
debug("fs.copyFileSync failed", error);
}
write(newPath, read(path));
}
export function exists(path: string): boolean {
debug("exists", path);
try {
return fs.existsSync(path);
} catch (error) {
debug("fs.existsSync failed", error);
}
return false;
}