A TypeScript library abstracting the Node filesystem APIs with virtual filesystems and cross-platform path handling
Concrete filesystem implementations that provide different behaviors and capabilities built on the @yarnpkg/fslib abstractions.
The package provides several filesystem implementations for different use cases:
Direct interface to the Node.js filesystem with portable path support.
import { NodeFS, type PortablePath } from '@yarnpkg/fslib';
// Create a NodeFS instance
const nodeFs = new NodeFS();
// Standard filesystem operations
const content = await nodeFs.readFilePromise('/path/to/file.txt' as PortablePath, 'utf8');
await nodeFs.writeFilePromise('/path/to/output.txt' as PortablePath, content);
// Directory operations
await nodeFs.mkdirPromise('/new/directory' as PortablePath, { recursive: true });
const files = await nodeFs.readdirPromise('/directory' as PortablePath);
// Metadata operations
const stats = await nodeFs.statPromise('/file.txt' as PortablePath);
console.log(`File size: ${stats.size}, Modified: ${stats.mtime}`);Compatibility wrapper that adds support for file URLs and Buffers to any filesystem. Primarily used internally for Node.js compatibility.
import { NodePathFS, NodeFS, type NativePath } from '@yarnpkg/fslib';
/**
* Adds support for file URLs and Buffers to the wrapped baseFs
* Only exists for compatibility with Node's behavior - avoid direct use
*/
class NodePathFS extends ProxiedFS<NativePath, NativePath> {
constructor(baseFs: FakeFS<NativePath>);
}
// Usage (typically internal)
const nodeFs = new NodeFS();
const pathFs = new NodePathFS(nodeFs);
// Now supports file URLs and Buffers (compatibility only)
const fileUrl = new URL('file:///path/to/file.txt');
const content = await pathFs.readFilePromise(fileUrl, 'utf8');
// Also supports Buffer paths (compatibility only)
const bufferPath = Buffer.from('/path/to/file.txt');
const data = await pathFs.readFilePromise(bufferPath, 'utf8');In-memory filesystem implementation for testing and isolated operations.
import { VirtualFS, ppath, type PortablePath } from '@yarnpkg/fslib';
// Create a virtual filesystem
const vfs = new VirtualFS();
// Create directory structure
await vfs.mkdirPromise('/project' as PortablePath, { recursive: true });
await vfs.mkdirPromise('/project/src' as PortablePath);
// Write virtual files
await vfs.writeFilePromise('/project/package.json' as PortablePath, JSON.stringify({
name: 'my-project',
version: '1.0.0'
}, null, 2));
await vfs.writeFilePromise('/project/src/index.ts' as PortablePath, 'console.log("Hello World");');
// Read from virtual filesystem
const packageContent = await vfs.readFilePromise('/project/package.json' as PortablePath, 'utf8');
const packageData = JSON.parse(packageContent);
console.log(`Project: ${packageData.name} v${packageData.version}`);
// List virtual directory contents
const sourceFiles = await vfs.readdirPromise('/project/src' as PortablePath);
console.log('Source files:', sourceFiles);Filesystem that restricts access to within a specific directory tree.
import { JailFS, NodeFS, type PortablePath } from '@yarnpkg/fslib';
// Create a jailed filesystem
const baseFs = new NodeFS();
const jailPath = '/safe/sandbox' as PortablePath;
const jailedFs = new JailFS(jailPath, { baseFs });
// All operations are restricted to the jail directory
await jailedFs.writeFilePromise('/file.txt' as PortablePath, 'content');
// Actually writes to /safe/sandbox/file.txt
await jailedFs.mkdirPromise('/subdir' as PortablePath);
// Actually creates /safe/sandbox/subdir
// Attempts to access outside the jail will fail
try {
await jailedFs.readFilePromise('/../../../etc/passwd' as PortablePath);
// This will throw an error or be blocked
} catch (error) {
console.log('Access denied outside jail');
}
// Safe operations within the jail
const files = await jailedFs.readdirPromise('/' as PortablePath);
console.log('Files in jail:', files);import { JailFS, type PortablePath } from '@yarnpkg/fslib';
interface JailFSOptions {
baseFs?: FakeFS<PortablePath>;
}
// Custom configuration
const jailedFs = new JailFS('/restricted/area' as PortablePath, {
baseFs: customFileSystem
});Filesystem that operates relative to a specific working directory.
import { CwdFS, NodeFS, type PortablePath } from '@yarnpkg/fslib';
// Create a filesystem rooted at a specific directory
const baseFs = new NodeFS();
const projectRoot = '/path/to/project' as PortablePath;
const cwdFs = new CwdFS(projectRoot, { baseFs });
// All relative paths are resolved relative to the project root
await cwdFs.writeFilePromise('config.json' as PortablePath, '{}');
// Actually writes to /path/to/project/config.json
await cwdFs.mkdirPromise('src/components' as PortablePath, { recursive: true });
// Creates /path/to/project/src/components
// Read files relative to the working directory
const configContent = await cwdFs.readFilePromise('config.json' as PortablePath, 'utf8');
// Absolute paths still work but are resolved from the working directory
const absoluteInCwd = await cwdFs.readFilePromise('/README.md' as PortablePath, 'utf8');
// Reads /path/to/project/README.mdimport { CwdFS, type PortablePath } from '@yarnpkg/fslib';
interface CwdFSOptions {
baseFs?: FakeFS<PortablePath>;
}
// Custom configuration
const cwdFs = new CwdFS('/working/directory' as PortablePath, {
baseFs: virtualFileSystem
});Filesystem that can mount other filesystems at specific paths.
import { MountFS, NodeFS, VirtualFS, type PortablePath } from '@yarnpkg/fslib';
// Create mount filesystem
const mountFs = new MountFS({ baseFs: new NodeFS() });
// Mount virtual filesystem at /virtual
const virtualFs = new VirtualFS();
await virtualFs.writeFilePromise('/data.txt' as PortablePath, 'virtual content');
mountFs.mount('/virtual' as PortablePath, virtualFs);
// Mount real directory at /real
const realFs = new NodeFS();
mountFs.mount('/real' as PortablePath, realFs);
// Access mounted filesystems
const virtualData = await mountFs.readFilePromise('/virtual/data.txt' as PortablePath, 'utf8');
console.log('Virtual content:', virtualData);
const realFiles = await mountFs.readdirPromise('/real' as PortablePath);
console.log('Real files:', realFiles);
// Unmount when done
mountFs.unmount('/virtual' as PortablePath);import { type MountableFS, type GetMountPointFn, type MountFSOptions } from '@yarnpkg/fslib';
// Mountable filesystem interface
interface MountableFS extends FakeFS<PortablePath> {
// Additional requirements for mountable filesystems
}
// Function to determine mount points
type GetMountPointFn<T extends MountableFS> = (
fs: T,
path: PortablePath
) => PortablePath | null;
// Mount filesystem options
interface MountFSOptions<T extends MountableFS> {
baseFs?: FakeFS<PortablePath>;
getMountPoint?: GetMountPointFn<T>;
}Filesystem that redirects specific paths to other locations.
import { AliasFS, NodeFS, type PortablePath } from '@yarnpkg/fslib';
// Create filesystem with path aliasing
const baseFs = new NodeFS();
const aliasMap = new Map<PortablePath, PortablePath>();
aliasMap.set('/alias/target' as PortablePath, '/real/location' as PortablePath);
const aliasFs = new AliasFS('/base/path' as PortablePath, aliasMap, { baseFs });
// Access through alias
const content = await aliasFs.readFilePromise('/alias/target/file.txt' as PortablePath, 'utf8');
// Actually reads from /real/location/file.txtFilesystem with deferred initialization.
import { LazyFS, NodeFS, type PortablePath } from '@yarnpkg/fslib';
// Create lazy filesystem that initializes on first use
const lazyFs = new LazyFS<PortablePath>(() => {
console.log('Initializing filesystem...');
return new NodeFS();
}, { baseFs: undefined });
// Filesystem is not initialized until first operation
const content = await lazyFs.readFilePromise('/file.txt' as PortablePath, 'utf8');
// Now the underlying NodeFS is created and usedFilesystem that handles conversion between native and portable paths.
import { PosixFS, type NativePath, type PortablePath } from '@yarnpkg/fslib';
// Create POSIX filesystem for cross-platform compatibility
const posixFs = new PosixFS();
// Works with native paths, converts internally
const nativePath = process.platform === 'win32' ? 'C:\\Windows\\System32' : '/usr/bin';
const files = await posixFs.readdirPromise(nativePath as NativePath);
console.log('System files:', files);Filesystem that denies all operations.
import { NoFS, type PortablePath } from '@yarnpkg/fslib';
// Create filesystem that throws errors for all operations
const noFs = new NoFS();
try {
await noFs.readFilePromise('/any/file.txt' as PortablePath, 'utf8');
} catch (error) {
console.log('Operation denied:', error.message);
}
// Useful as a placeholder or for securityAbstract base class for filesystems that proxy to another filesystem.
import { ProxiedFS, type PortablePath, type NativePath } from '@yarnpkg/fslib';
// Create a custom proxied filesystem
class LoggingFS extends ProxiedFS<PortablePath, PortablePath> {
constructor(baseFs: FakeFS<PortablePath>) {
super(baseFs);
}
async readFilePromise(p: PortablePath, encoding?: BufferEncoding): Promise<Buffer | string> {
console.log(`Reading file: ${p}`);
const result = await this.baseFs.readFilePromise(p, encoding);
console.log(`Read ${Buffer.isBuffer(result) ? result.length : result.length} bytes/characters`);
return result;
}
async writeFilePromise(p: PortablePath, content: string | Buffer): Promise<void> {
console.log(`Writing file: ${p}`);
await this.baseFs.writeFilePromise(p, content);
console.log(`Write completed`);
}
// Other methods automatically proxy to baseFs
}
// Usage
const baseFs = new NodeFS();
const loggingFs = new LoggingFS(baseFs);
await loggingFs.writeFilePromise('/log/file.txt' as PortablePath, 'content');
// Logs: Writing file: /log/file.txt
// Logs: Write completedimport {
MountFS, JailFS, VirtualFS, NodeFS,
type PortablePath
} from '@yarnpkg/fslib';
// Create a complex filesystem setup
async function createDevelopmentEnvironment(): Promise<MountFS> {
const mountFs = new MountFS({ baseFs: new NodeFS() });
// Mount real project directory at /project (jailed for security)
const projectFs = new JailFS('/real/project/path' as PortablePath, {
baseFs: new NodeFS()
});
mountFs.mount('/project' as PortablePath, projectFs);
// Mount virtual filesystem for temporary files at /tmp
const tmpFs = new VirtualFS();
mountFs.mount('/tmp' as PortablePath, tmpFs);
// Mount system tools (read-only jail) at /tools
const toolsFs = new JailFS('/usr/local/bin' as PortablePath, {
baseFs: new NodeFS()
});
mountFs.mount('/tools' as PortablePath, toolsFs);
return mountFs;
}
// Usage
const devEnv = await createDevelopmentEnvironment();
await devEnv.writeFilePromise('/project/src/main.ts' as PortablePath, 'code');
await devEnv.writeFilePromise('/tmp/cache.json' as PortablePath, '{}');
const tools = await devEnv.readdirPromise('/tools' as PortablePath);import { VirtualFS, type PortablePath } from '@yarnpkg/fslib';
// Set up test filesystem
async function setupTestEnvironment(): Promise<VirtualFS> {
const testFs = new VirtualFS();
// Create test project structure
await testFs.mkdirPromise('/test-project' as PortablePath, { recursive: true });
await testFs.mkdirPromise('/test-project/src' as PortablePath);
await testFs.mkdirPromise('/test-project/tests' as PortablePath);
// Add test files
await testFs.writeFilePromise(
'/test-project/package.json' as PortablePath,
JSON.stringify({ name: 'test-pkg', version: '1.0.0' }, null, 2)
);
await testFs.writeFilePromise(
'/test-project/src/index.ts' as PortablePath,
'export function hello() { return "world"; }'
);
await testFs.writeFilePromise(
'/test-project/tests/index.test.ts' as PortablePath,
'import { hello } from "../src"; console.assert(hello() === "world");'
);
return testFs;
}
// Use in tests
describe('File operations', () => {
let testFs: VirtualFS;
beforeEach(async () => {
testFs = await setupTestEnvironment();
});
it('should read package.json', async () => {
const content = await testFs.readFilePromise('/test-project/package.json' as PortablePath, 'utf8');
const pkg = JSON.parse(content);
expect(pkg.name).toBe('test-pkg');
});
});import {
VirtualFS, NodeFS,
type FakeFS, type PortablePath
} from '@yarnpkg/fslib';
// Copy directory tree between filesystems
async function copyDirectoryTree(
sourceFs: FakeFS<PortablePath>,
targetFs: FakeFS<PortablePath>,
sourcePath: PortablePath,
targetPath: PortablePath
): Promise<void> {
const entries = await sourceFs.readdirPromise(sourcePath, { withFileTypes: true });
await targetFs.mkdirPromise(targetPath, { recursive: true });
for (const entry of entries) {
const srcPath = ppath.join(sourcePath, entry.name);
const dstPath = ppath.join(targetPath, entry.name);
if (entry.isDirectory()) {
await copyDirectoryTree(sourceFs, targetFs, srcPath, dstPath);
} else if (entry.isFile()) {
const content = await sourceFs.readFilePromise(srcPath);
await targetFs.writeFilePromise(dstPath, content);
}
}
}
// Usage: migrate from virtual to real filesystem
const virtualFs = new VirtualFS();
const nodeFs = new NodeFS();
// ... populate virtualFs ...
await copyDirectoryTree(
virtualFs,
nodeFs,
'/virtual/project' as PortablePath,
'/real/backup' as PortablePath
);Install with Tessl CLI
npx tessl i tessl/npm-yarnpkg--fslib