A minimal virtual filesystem for test suites. 306 LOC, zero runtime dependencies.
# whatever pkg manager you want
npm i tiny-virtfsimport VirtFS from "tiny-virtfs";
// Create a virtual filesystem
const fs = new VirtFS();
// Basic operations
fs.write("/src/index.ts", 'export const hello = "world"');
fs.write("/src/utils/helper.ts", "export const help = () => {}");
fs.mkdir("/dist");
// Read files
const content = fs.readString("/src/index.ts"); // "export const hello = "world""
// Check existence
fs.exists("/src/index.ts"); // true
fs.isFile("/src"); // false
fs.isDir("/src"); // true
// List directory
fs.list("/src"); // ['index.ts', 'utils']
// Copy/move
fs.copy("/src/index.ts", "/backup/index.ts");
fs.move("/src/utils", "/src/lib");
// Remove
fs.remove("/backup");
// Convert to plain object (great for snapshots)
fs.toTree();
// { src: { 'index.ts': 'export const hello = "world"', lib: { ... } } }
// Create from tree
const fs2 = VirtFS.fromTree({
"package.json": '{"name": "my-app"}',
src: {
"index.ts": 'console.log("hello")',
},
});
// Dump to real filesystem (useful for test fixtures)
await fs.dumpToDisk("./test-output");
// Load from real filesystem
const fs3 = await VirtFS.fromDisk("./my-project");write(path, content)- Write a file (creates parent dirs automatically)read(path)- Read file content (returnsstring | Buffer | undefined)readString(path)- Read as string (converts Buffer to UTF-8)
mkdir(path)- Create directory (and all parents)list(path)- List directory contentsremove(path)- Remove file or directory
exists(path)- Check if path existsisFile(path)- Check if path is a fileisDir(path)- Check if path is a directory
copy(src, dest)- Copy file or directorymove(src, dest)- Move file or directory
toTree()- Export to plain object (great for snapshots)VirtFS.fromTree(obj)- Create from plain object
dumpToDisk(path)- Async: Write virtual FS to real directorydumpToDiskSync(path)- Sync: Write virtual FS to real directoryVirtFS.fromDisk(path)- Async: Load real directory into virtual FS
mockNodeFS()- Intercept supportednode:fsandnode:fs/promisescalls, returns restore function- Sync:
readFileSync,writeFileSync,existsSync,statSync,lstatSync,mkdirSync,readdirSync,rmSync,unlinkSync,rmdirSync,cpSync,renameSync,accessSync - Async/promises:
readFile,writeFile,mkdir,readdir,stat,lstat,rm,unlink,rmdir,cp,rename,access
Run with bun benchmark.ts on this machine. Higher is better.
| Operation | tiny-virtfs | memfs | memory-fs | tiny vs memfs | tiny vs memory-fs |
|---|---|---|---|---|---|
| write | 6243.66k ops/s | 137.17k ops/s | 1172.22k ops/s | 45.5x | 5.3x |
| read | 4682.56k ops/s | 101.87k ops/s | 842.03k ops/s | 46.0x | 5.6x |
| exists | 1882.00k ops/s | 147.27k ops/s | 812.28k ops/s | 12.8x | 2.3x |
| mkdir | 1582.89k ops/s | 80.73k ops/s | 1424.45k ops/s | 19.6x | 1.1x |
| deep write | 1277.19k ops/s | 43.16k ops/s | 471.70k ops/s | 29.6x | 2.7x |
| many files | 63.02k ops/s | 1.91k ops/s | 18.33k ops/s | 33.0x | 3.4x |
clear()- Remove all files
Perfect for:
- Testing file transformations (compilers, bundlers, generators)
- Mocking filesystem operations
- Creating test fixtures
- Snapshot testing file structures
import VirtFS from "tiny-virtfs";
// Your function that internally uses node:fs
function readConfig(path: string) {
const fs = require("node:fs");
return JSON.parse(fs.readFileSync(path, "utf-8"));
}
describe("readConfig", () => {
it("parses config file", () => {
const vfs = new VirtFS();
vfs.write("/config.json", '{"port": 3000}');
// Redirect all node:fs calls to virtual filesystem
const restore = vfs.mockNodeFS();
const config = readConfig("/config.json");
expect(config.port).toBe(3000);
// Restore real node:fs
restore();
});
});import VirtFS from "tiny-virtfs";
describe("component generator", () => {
it("generates a button component", async () => {
const fs = new VirtFS();
// Simulate shadcn output
fs.write(
"/components/ui/button.tsx",
`
export function Button({ children }) {
return <button>{children}</button>;
}
`,
);
// Write to real filesystem for manual inspection
await fs.dumpToDisk("./test-output");
// Or snapshot the tree
expect(fs.toTree()).toMatchSnapshot();
});
});MIT