Type-safe, ergonomic package for working with paths and files in Node.js
Instead of juggling raw strings with node:path and node:fs, this package makes file paths first-class citizens in your code -- with type-safe manipulation and convenient filesystem methods.
- Immutable, chainable path objects with type-safe operations
- Path normalization and resolution on construction
- Easy access to path parts (filename, stem, extension, parent, etc)
- Path transformations (replace stem/extension/parent, transform filename, etc)
- Path navigation (join, resolve, relativeTo, descendsFrom, etc)
- Async filesystem operations (exists, isFile, isDirectory, stat, read, write, makeDirectory, readDirectory, glob, etc)
- Temporary directory & file management
Together, these features give you a safer, more expressive way to work with paths, files, and directories in Node.js
Under the hood, this package uses:
@thingts/pathfor path manipulationnode:fs/promisesfor async filesystem operationsfast-globfor globbing
@thingts/fs-path provides a set of classes to represent and manipulate filesystem paths. All classes are immutable; any path manipulation operation returns a new instance.
This package defines:
FsPath- An absolute filesystem path object, with path manipulation and filesystem operations.FsPathextendsAbsolutePathfrom@thingts/pathwhich provides path manipulation, and adds the filesystem operations.
For convenience this package also re-exports these two classes from
@thingts/path:
RelativePath- Relative path object with path manipulation.Filename- Filename object with file part manipulation.
The classes work together to maintain type safety and ergonomics. For
example, the .relativeTo() method of FsPath returns a RelativePath
-- which would need to be joined to a base FsPath in order to
perform filesystem operations.
⚠️ Currently only POSIX-style paths are supported (e.g./foo/bar).
🔧 This package supports most commonly used
node:fsfeatures & options. But not all; contributions to expand functionality are welcome.
npm install @thingts/fs-pathThis is a quick overview of some common operations. For complete docs, see the API Reference.
import { FsPath } from '@thingts/fs-path'const a = new FsPath('/foo/../bar/file.txt')
a.equals('/bar/file.txt') // true
const b = new FsPath('relative/to/cwd.txt')
b.equals(FsPath.cwd().join('relative/to/cwd.txt')) // trueconst a = new FsPath('/bar/file.txt')
a.filename // Filename: 'file.txt'
a.filename.toString() // string: 'file.txt'
a.stem // string: 'file'
a.extension // string: '.txt'
a.parent // FsPath: '/bar'
const b = a.replaceStem('report') // FsPath: '/bar/report.txt'
const c = b.replaceExtension('.md') // FsPath: '/bar/report.md'
const d = c.replaceParent('/other') // FsPath: '/other/report.md'
const e = d.transformFilename(f => String(f).toUpperCase()) // FsPath: '/other/REPORT.MD'const base = new FsPath('/projects/demo')
base.join('src/index.ts') // FsPath: '/projects/demo/src/index.ts'
base.descendsFrom('/projects') // true
base.parent.equals('/projects') // true
const rel = base.join('src/main.ts').relativeTo(base) // RelativePath: 'src/main.ts'const dir = new FsPath('/projects/demo')
const file = dir.join('logs/app.log')
// --- Writing and reading ---
await file.write('start\n', { makeParents: true })
await file.write('listening\n', { append: true })
await file.read() // string: 'start\nlistening\n'
// --- File info ---
await file.exists() // true
await file.isFile() // true
await file.isDirectory() // false
await file.parent.isDirectory() // true
await file.stat() // fs.Stats object
// --- Directory operations...
await dir.join('sub').makeDirectory()
const files = await dir.readDirectory() // [FsPath, ...]
const txts = await dir.glob('**/*.log') // glob within a directory// --- Explicit resource management ---
{
using dir = await FsPath.makeTempDirectory() // returns disposable directory
using file = new FsPath('/project/tempfile.txt').disposable() // register for disposal
dir.exists() // true
file.write('data') // create file
...
// dir and file are removed when they go out of scope
}
// --- Removed eventually, on gc or exit ---
const dir = await FsPath.makeTempDirectory()
const file = new FsPath('/project/tempfile.txt').disposable()- @thingts/path – Path manipulation only (no fs), pure javascript, no node.js dependences (browser-safe)
Contributions are welcome!
As usual: fork the repo, create a feature branch, and open a pull request, with tests and docs for any new functionality. Thanks!