diff --git a/docs/runtime/nodejs-apis.md b/docs/runtime/nodejs-apis.md index 63075409b3..9228644889 100644 --- a/docs/runtime/nodejs-apis.md +++ b/docs/runtime/nodejs-apis.md @@ -56,7 +56,7 @@ Some methods are not optimized yet. ### [`node:fs`](https://nodejs.org/api/fs.html) -🟡 Missing `Dir` `openAsBlob` `opendir` `opendirSync` `statfs` `statfsSync` +🟡 Missing `statfs` `statfsSync`, `opendirSync`. `Dir` is partially implemented. ### [`node:http`](https://nodejs.org/api/http.html) diff --git a/src/js/node/fs.js b/src/js/node/fs.js index caaec55eef..d6033efcf7 100644 --- a/src/js/node/fs.js +++ b/src/js/node/fs.js @@ -95,6 +95,10 @@ class FSWatcher extends EventEmitter { // close(); // } +function openAsBlob(path, options) { + return Promise.$resolve(Bun.file(path, options)); +} + class StatWatcher extends EventEmitter { // _handle: StatWatcherHandle; @@ -326,44 +330,39 @@ var access = function access(...args) { lutimesSync = fs.lutimesSync.bind(fs), rmSync = fs.rmSync.bind(fs), rmdirSync = fs.rmdirSync.bind(fs), - writev = (fd, buffers, position, callback) => { + writev = function writev(fd, buffers, position, callback) { if (typeof position === "function") { callback = position; position = null; } - queueMicrotask(() => { - try { - var written = fs.writevSync(fd, buffers, position); - } catch (e) { - callback(e); - } + if (!$isCallable(callback)) { + throw new TypeError("callback must be a function"); + } - callback(null, written, buffers); - }); + fs.writev(fd, buffers, position).$then(bytesWritten => callback(null, bytesWritten, buffers), callback); }, writevSync = fs.writevSync.bind(fs), - readv = (fd, buffers, position, callback) => { + readv = function readv(fd, buffers, position, callback) { if (typeof position === "function") { callback = position; position = null; } - queueMicrotask(() => { - try { - var written = fs.readvSync(fd, buffers, position); - } catch (e) { - callback(e); - } + if (!$isCallable(callback)) { + throw new TypeError("callback must be a function"); + } - callback(null, written, buffers); - }); + fs.readv(fd, buffers, position).$then(bytesRead => callback(null, bytesRead, buffers), callback); }, readvSync = fs.readvSync.bind(fs), Dirent = fs.Dirent, Stats = fs.Stats, watch = function watch(path, options, listener) { return new FSWatcher(path, options, listener); + }, + opendir = function opendir(...args) { + callbackify(promises.opendir, args); }; // TODO: make symbols a separate export somewhere @@ -1349,6 +1348,8 @@ export default { writevSync, fdatasync, fdatasyncSync, + openAsBlob, + opendir, [Symbol.for("::bunternal::")]: { ReadStreamClass, WriteStreamClass, diff --git a/test/js/node/fs/fs.test.ts b/test/js/node/fs/fs.test.ts index 5045023653..d8bf6b1145 100644 --- a/test/js/node/fs/fs.test.ts +++ b/test/js/node/fs/fs.test.ts @@ -37,6 +37,7 @@ import fs, { readvSync, fstatSync, fdatasyncSync, + openAsBlob, } from "node:fs"; import _promises, { type FileHandle } from "node:fs/promises"; @@ -45,7 +46,7 @@ import { tmpdir } from "node:os"; import { join } from "node:path"; import { ReadStream as ReadStream_, WriteStream as WriteStream_ } from "./export-from.js"; -import { ReadStream as ReadStreamStar_, WriteStream as WriteStreamStar_, fdatasync } from "./export-star-from.js"; +import { Dir, ReadStream as ReadStreamStar_, WriteStream as WriteStreamStar_, fdatasync } from "./export-star-from.js"; import { spawnSync } from "bun"; const Buffer = globalThis.Buffer || Uint8Array; @@ -59,6 +60,10 @@ function mkdirForce(path: string) { if (!existsSync(path)) mkdirSync(path, { recursive: true }); } +it("fs.openAsBlob", async () => { + expect((await openAsBlob(import.meta.path)).size).toBe(statSync(import.meta.path).size); +}); + it("writing to 1, 2 are possible", () => { expect(fs.writeSync(1, Buffer.from("\nhello-stdout-test\n"))).toBe(19); expect(fs.writeSync(2, Buffer.from("\nhello-stderr-test\n"))).toBe(19); @@ -2291,6 +2296,13 @@ describe("fs/promises", () => { it("opendir should have a path property, issue#4995", async () => { expect((await fs.promises.opendir(".")).path).toBe("."); + + const { promise, resolve } = Promise.withResolvers