@wtf/platform-fs-memory
Documentation / @wtf/platform-fs-memory
A file system in memory for your tests and dry runs. It gives you folder handles of the WHATWG File System Standard, so you can pass it wherever your code takes a browser’s handle or Node’s.
| Subpath | What you get |
|---|---|
. | MemoryDirectoryOptions, the options it takes; MemoryNode, MemoryChange and the other types of what it keeps |
./handles | memoryDirectory({ ignoreCase, now }): the root of a new, empty file system; the handles’ classes, to extend |
./tree | Tree: the entries of a file system, found by their paths, which a class that extends the handles reaches as fileSystem.tree |
import { memoryDirectory } from '@wtf/platform-fs-memory/handles'
const root = memoryDirectory()const file = await root.getFileHandle('notes.md', { create: true })await new Blob(['# Notes\n']).stream().pipeTo(await file.createWritable())- Every call behaves as the standard says a handle behaves, and fails with the standard’s
DOMExceptions. Each file system is separate, and none outlives its handles. - A writable writes to bytes of its own, which replace the file when it closes. An entry with an
open writable cannot be removed or moved: that is a
NoModificationAllowedError. A snapshot is aFileholding a copy of the bytes, so it reads what the file held then, even after the file is written. - A file’s
createSyncAccessHandle()reads and writes its bytes in place, and holds the file until it closes, as a writable does. ignoreCasefinds an entry by a name in another case, or another Unicode form, as macOS and Windows do. An entry keeps the name it was created with, andisSameEntrytreats the two names as one entry.nowstamps each change,Date.now()by default, so a test can set the time.
To give memoryPlatform of @wtf/platform-memory a file system, pass it a folder:
import { memoryPlatform } from '@wtf/platform-memory/platform'import { memoryDirectory } from '@wtf/platform-fs-memory/handles'
const platform = memoryPlatform({ directory: () => memoryDirectory() })const root = await platform.navigator.storage.getDirectory()Extend the handles
The handles are classes you can extend, to add calls of your own to every handle of a file system:
MemoryFileHandle and MemoryDirectoryHandle, which both extend MemoryHandle.
- A folder makes the handle of each entry in it through one method,
handleOf(parts, kind). Override it in your folder’s class to make your own classes, and every entry, however deep, is one of yours. MemoryDirectoryHandle.root(options), called on your class, makes the root of a new file system of your class.- Your class reaches what a handle is of through its protected members:
fileSystem, its entries (fileSystem.tree) and the listeners of its changes (fileSystem.listeners);parts, the names of the entry from the root;node(), the entry, which keeps its mode and its times, and where it is; andemit(change), which tells each listener of a change you make. An observer of the file system adds itself to its listeners.
import type { FileSystemHandleKind } from '@wtf/platform/fs'import { MemoryDirectoryHandle, MemoryFileHandle } from '@wtf/platform-fs-memory/handles'
class ModeFileHandle extends MemoryFileHandle { async getMode(): Promise<number> { return this.node().node.mode & 0o7777 }}
class ModeDirectoryHandle extends MemoryDirectoryHandle { protected override handleOf(parts: readonly string[], kind: FileSystemHandleKind) { return kind === 'file' ? new ModeFileHandle(this.fileSystem, parts) : new ModeDirectoryHandle(this.fileSystem, parts) }}
const root = ModeDirectoryHandle.root()const file = (await root.getFileHandle('run.sh', { create: true })) as ModeFileHandleawait file.getMode() // 0o644The package depends on @wtf/platform-fs. Its tests run the file system suite on it, both
case-sensitive and ignoring case, and the Web Platform Tests of fs/, with their known failures in
test/wpt/fs.json.
