The file-system module
Reference for node:fs and node:fs/promises in Cruft: reading and writing files, stat and permissions, directories and glob, watching, and fd-level I/O, in synchronous, callback, and promise forms. States which methods are real operations and which are shape-only stubs. All access is capability-gated.
Filesystem access in Cruft, exposed the way Node exposes it: synchronous (…Sync), Node-style callback, and promise (node:fs/promises) forms of the same operations. Per the compatibility map this module is at the focused depth tier: read/write, stat, readdir, glob, watch, and fd-level I/O are implemented as real operations; binary reads return a real Node Buffer (ArrayBuffer.isView is true, instanceof Uint8Array is true, and constructor.name is "Buffer"). Errors on the ordinary missing-file path carry Node-style .code (for example ENOENT), plus errno, syscall, and path where applicable. All operations are capability-gated through the runtime's capability layer. This page enumerates what is actually installed; anything not listed here is not present.
Import forms
All four specifiers resolve, in both module systems:
// ESM
import fs from "node:fs";
import fs from "fs";
import { readFile } from "node:fs/promises";
import { readFile } from "fs/promises";
// CJS
const fs = require("node:fs"); // or require("fs")
const fsp = require("node:fs/promises"); // or require("fs/promises")
node:fs/promises is a distinct namespace (the promise API, not all of fs); it is also reachable as fs.promises.
Reading and writing files
| Function | Semantics |
|---|---|
readFileSync(path[, options]) | Read a file. With an encoding, returns a string; without, returns a real Node Buffer (instanceof Uint8Array is true, constructor.name is "Buffer"). |
readFile(path[, options], cb) | Callback form; (err, data). |
writeFileSync(path, data[, options]) | Write a file (string or bytes). |
writeFile(path, data[, options], cb) | Callback form. |
appendFileSync(path, data[, options]) / appendFile(path, data, cb) | Append. |
copyFileSync(src, dest[, mode]) / copyFile(src, dest, cb) | Copy one file. |
cpSync(src, dest[, options]) / cp(src, dest, cb) | Copy; { recursive: true } copies a directory tree. |
truncateSync(path[, len]) / truncate(path, cb) | Truncate a file. |
renameSync(old, new) / rename(old, new, cb) | Rename/move. |
openAsBlob(path) | Promise of a Blob-shaped object: size, type, text(), arrayBuffer(), bytes(). |
createReadStream(path[, options]) | Event-emitter read stream: on/once, data/end/error events, pipe, close, destroy, pause, resume, bytesRead. |
createWriteStream(path[, options]) | Event-emitter write stream: write, end, destroy, bytesWritten, open/finish/drain events. |
Round-trip:
import fs from "node:fs";
fs.writeFileSync("/tmp/fsdemo/hello.txt", "hello cruft\n");
console.log(JSON.stringify(fs.readFileSync("/tmp/fsdemo/hello.txt", "utf8")));
$ cruft /tmp/demo.mjs
"hello cruft\n"
Binary reads return a real Node Buffer:
import fs from "node:fs";
const buf = fs.readFileSync("/tmp/fsdemo/hello.txt");
console.log(buf.constructor.name);
console.log(buf instanceof Uint8Array);
console.log(ArrayBuffer.isView(buf));
console.log(Array.from(buf.slice(0, 5)));
$ cruft /tmp/demo.mjs
Buffer
true
true
[ 104, 101, 108, 108, 111 ]
Metadata and permissions
| Function | Semantics |
|---|---|
statSync(path[, options]) / stat(path, cb) | Stats object: size, mode, uid, gid, mtimeMs, atimeMs, ctimeMs, birthtimeMs, date fields, and isFile() / isDirectory() / isSymbolicLink() predicates. isBlockDevice/isCharacterDevice/isFIFO/isSocket are present and return false. |
lstatSync(path) / lstat(path, cb) | Stat without following symlinks. |
fstatSync(fd) / fstat(fd, cb) | Stat an open fd. |
statfsSync(path) / statfs(path, cb) | Filesystem stats (fixed-shape object: type, bsize, blocks, bfree, bavail, files, ffree). |
existsSync(path) / exists(path, cb) | Existence check. |
accessSync(path[, mode]) / access(path, cb) | Throws/errors if not accessible. |
chmodSync / chmod, fchmodSync / fchmod, lchmodSync / lchmod | Mode changes (path, fd, no-follow). |
chownSync / lchownSync / fchownSync (+ chown, lchown, fchown callback forms) | Ownership changes; the callback forms wrap the …Sync impls (their callbacks fire with real results/errors). |
utimesSync / utimes, lutimesSync / lutimes, futimesSync / futimes | Timestamps. |
realpathSync(path) / realpath(path, cb) | Canonicalize; both carry a .native sub-property as in Node. |
readlinkSync(path) / readlink(path, cb) | Read a symlink target. |
linkSync / link, symlinkSync / symlink | Hard/symbolic links. |
constants | POSIX constants: F_OK/R_OK/W_OK/X_OK, O_* open flags, S_IF* and S_I* mode bits, COPYFILE_*, UV_DIRENT_*. |
import fs from "node:fs";
const st = fs.statSync("/tmp/fsdemo/hello.txt");
console.log(st.size, st.isFile(), st.isDirectory());
$ cruft /tmp/demo.mjs
12 true false
Directories and glob
| Function | Semantics |
|---|---|
mkdirSync(path[, options]) / mkdir(path, cb) | Make a directory; { recursive: true } makes the whole path. |
readdirSync(path[, options]) / readdir(path, cb) | List names; { withFileTypes: true } yields Dirent objects with name and type predicates. |
rmSync(path[, options]) / rm(path, cb) | Remove; { recursive: true } removes a tree. |
rmdirSync(path) / rmdir(path, cb) | Remove an empty directory. |
unlinkSync(path) / unlink(path, cb) | Remove a file. |
mkdtempSync(prefix) / mkdtemp(prefix, cb) | Temporary directory. mkdtempDisposableSync / mkdtempDisposable also present. |
opendirSync(path) / opendir(path, cb) | Dir object with a read() cursor over Dirents. |
globSync(pattern) / glob(pattern, cb) | Shell-pattern match, resolved against and returned relative to the cwd; * stays within a segment, ** crosses segments. |
import fs from "node:fs";
// cwd is /tmp/fsdemo
console.log(fs.globSync("*.txt").sort());
console.log(fs.globSync("**/*.txt").sort());
$ cruft /tmp/demo.mjs
[ 'h2.txt', 'hello.txt', 'p.txt', 'w.txt' ]
[ 'h2.txt', 'hello.txt', 'p.txt', 'sub/a.txt', 'sub/b.txt', 'w.txt' ]
Watching
| Function | Semantics |
|---|---|
watch(path[, options][, listener]) | Returns a watcher object with close, on, ref, unref. Changes are detected by mtime/size polling and delivered as macrotasks. |
watchFile(path[, options], listener) | Stat-polling variant. |
unwatchFile(path) | Deregisters watchers for a path. |
import fs from "node:fs";
const w = fs.watch("/tmp/fsdemo");
console.log(typeof w.close, typeof w.on, typeof w.ref);
w.close();
$ cruft /tmp/demo.mjs
function function function
fd-level I/O
| Function | Semantics |
|---|---|
openSync(path[, flags[, mode]]) / open(path, cb) | Integer fd into the runtime's fd table. |
readSync(fd, buffer, offset, length, position) / read(fd, …, cb) | Fill a buffer from an fd; returns bytes read. |
writeSync(fd, buffer, …) / write(fd, …, cb) | Write via an fd. |
readvSync(fd, buffers) / writevSync(fd, buffers) | Scatter-gather I/O; sync, top-level callback (readv/writev), and promise forms all work (callbacks wrap the …Sync impls). |
closeSync(fd) / close(fd, cb) | Release the fd. |
fsyncSync / fsync, fdatasyncSync / fdatasync, ftruncateSync / ftruncate | fd-level sync/truncate. |
import fs from "node:fs";
const fd = fs.openSync("/tmp/fsdemo/hello.txt", "r");
const buf = new Uint8Array(5);
const n = fs.readSync(fd, buf, 0, 5, 0);
fs.closeSync(fd);
console.log(n, Array.from(buf));
$ cruft /tmp/demo.mjs
5 [ 104, 101, 108, 108, 111 ]
Promises API (node:fs/promises)
Every callback-form method above is mirrored as a promise on fs/promises (and fs.promises), plus constants:
Differences from Node worth knowing:
open(path, flags)resolves a minimal FileHandle:fd,stat(),close(). Deeper FileHandle methods (read,write,readFile,createReadStream, …) are not on the handle.glob(pattern)returns an async iterator (Node-faithful): iterate it withfor await (… of glob(…)); it is not an array, soArray.isArrayisfalseand it has noSymbol.iterator.promises.watchreturns an event-emitter watcher (close,on,off,ref,unref) that holds the loop open — not an async iterator (noSymbol.asyncIterator), and notundefined. Itsref/unrefare no-ops: the watcher keeps the loop alive until youclose()it,unrefnotwithstanding.
import { readFile, writeFile, glob } from "node:fs/promises";
await writeFile("/tmp/fsdemo/p.txt", "promise data\n");
console.log(JSON.stringify(await readFile("/tmp/fsdemo/p.txt", "utf8")));
const matches = [];
for await (const m of glob("*.txt")) matches.push(m); // async iterator, cwd /tmp/fsdemo
console.log(matches.includes("p.txt"));
$ cruft /tmp/demo.mjs
"promise data\n"
true
Not implemented / gaps
This list reflects the installed API surface. Unlisted APIs are not present. Do not assume Node parity beyond what this page documents.
- Callback wrappers over the
…Syncimpls: top-levelchown,lchmod,lchown,readv, andwritevcallback forms are functional (their callbacks fire with real results/errors) rather than throw-on-call stubs. - Class stubs:
Stats,Dirent,Dirare present but not constructable (constructor throws).ReadStream,WriteStream,FileReadStream,FileWriteStream,Utf8Streamexist as shape-only constructor stubs; usecreateReadStream/createWriteStreamfor real streams. - FileHandle is minimal:
fd,stat(),close()only (see above). - No BigInt stats:
{ bigint: true }options are not honored; stats fields are numbers. - No
fs.Direntrecursion options beyond what is documented;readdirSync{ recursive: true }behavior beyond names/Dirents should be probed before relying on it. promises.watchreturns an event-emitter watcher (close/on/off/ref/unref) with no async-iterator support, notundefined.- Device/FIFO/socket stat predicates always return
false. openAsBlobreports an emptytype("").