A TypeScript library abstracting the Node filesystem APIs with virtual filesystems and cross-platform path handling
Cross-platform path manipulation utilities that provide type-safe path operations and conversion between different path formats.
The @yarnpkg/fslib package provides comprehensive path handling through two main utilities:
ppath - Portable path utilities for cross-platform compatibilitynpath - Native path utilities with platform-specific behavior// Basic path types
type PortablePath = string & { readonly __portable_path__: unique symbol };
type NativePath = string & { readonly __native_path__: unique symbol };
type Filename = string & { readonly __filename__: unique symbol };
type Path = PortablePath | NativePath;
type FSPath<T> = T | number; // Path or file descriptor
// Path component types
interface ParsedPath<P extends Path> {
root: P;
dir: P;
base: Filename;
ext: string;
name: string;
}
interface FormatInputPathObject<P extends Path> {
root?: P;
dir?: P;
base?: Filename;
ext?: string;
name?: string;
}The ppath utility provides cross-platform path operations that always use forward slashes as separators.
import { ppath, type PortablePath } from '@yarnpkg/fslib';
// Join path segments
const joined = ppath.join('/base' as PortablePath, 'sub', 'file.txt');
// Result: '/base/sub/file.txt' as PortablePath
// Resolve to absolute path
const resolved = ppath.resolve('/current' as PortablePath, '../relative/path');
// Result: '/relative/path' as PortablePath
// Normalize path
const normalized = ppath.normalize('/path/./to/../file.txt' as PortablePath);
// Result: '/path/file.txt' as PortablePathimport { ppath, type PortablePath } from '@yarnpkg/fslib';
// Check if path is absolute
const isAbsolute = ppath.isAbsolute('/absolute/path' as PortablePath);
// Result: true
// Get relative path between two paths
const relative = ppath.relative(
'/from/directory' as PortablePath,
'/to/file.txt' as PortablePath
);
// Result: '../to/file.txt' as PortablePath
// Check if one path contains another
const contains = ppath.contains(
'/parent' as PortablePath,
'/parent/child/file.txt' as PortablePath
);
// Result: trueimport { ppath, type PortablePath, type Filename } from '@yarnpkg/fslib';
const filePath = '/path/to/file.txt' as PortablePath;
// Get directory name
const dirname = ppath.dirname(filePath);
// Result: '/path/to' as PortablePath
// Get base name
const basename = ppath.basename(filePath);
// Result: 'file.txt' as Filename
const basenameNoExt = ppath.basename(filePath, '.txt');
// Result: 'file' as Filename
// Get file extension
const extension = ppath.extname(filePath);
// Result: '.txt'import { ppath, type PortablePath } from '@yarnpkg/fslib';
// Parse path into components
const parsed = ppath.parse('/path/to/file.txt' as PortablePath);
// Result: {
// root: '/' as PortablePath,
// dir: '/path/to' as PortablePath,
// base: 'file.txt' as Filename,
// ext: '.txt',
// name: 'file'
// }
// Format path from components
const formatted = ppath.format({
dir: '/path/to' as PortablePath,
name: 'file',
ext: '.txt'
});
// Result: '/path/to/file.txt' as PortablePathimport { ppath } from '@yarnpkg/fslib';
// Get current working directory
const cwd = ppath.cwd();
// Result: Current working directory as PortablePathThe npath utility provides platform-specific path operations and conversion utilities.
import { npath, type NativePath } from '@yarnpkg/fslib';
// All standard path operations (same interface as ppath)
const joined = npath.join('/base' as NativePath, 'sub', 'file.txt');
const resolved = npath.resolve('/current' as NativePath, '../relative/path');
const normalized = npath.normalize('/path/./to/../file.txt' as NativePath);import { npath, type PortablePath, type NativePath } from '@yarnpkg/fslib';
// Convert portable path to native path
const portablePath = '/path/to/file.txt' as PortablePath;
const nativePath = npath.fromPortablePath(portablePath);
// On Windows: 'C:\\path\\to\\file.txt' as NativePath
// On Unix: '/path/to/file.txt' as NativePath
// Convert native path to portable path
const backToPortable = npath.toPortablePath(nativePath);
// Result: '/path/to/file.txt' as PortablePathimport { PortablePath } from '@yarnpkg/fslib';
// Root path
const root = PortablePath.root; // '/' as PortablePath
// Current directory
const dot = PortablePath.dot; // '.' as PortablePath
// Parent directory
const parent = PortablePath.parent; // '..' as PortablePathimport { Filename } from '@yarnpkg/fslib';
// Common directory and file names
const home = Filename.home; // '~' as Filename
const nodeModules = Filename.nodeModules; // 'node_modules' as Filename
const packageJson = Filename.manifest; // 'package.json' as Filename
const yarnLock = Filename.lockfile; // 'yarn.lock' as Filename
// Yarn-specific files
const virtual = Filename.virtual; // '__virtual__' as Filename
const pnpCjs = Filename.pnpCjs; // '.pnp.cjs' as Filename
const pnpData = Filename.pnpData; // '.pnp.data.json' as Filename
const pnpLoader = Filename.pnpEsmLoader; // '.pnp.loader.mjs' as Filename
const yarnrc = Filename.rc; // '.yarnrc.yml' as Filename
const env = Filename.env; // '.env' as Filenameimport { convertPath, ppath, npath, type PortablePath, type NativePath } from '@yarnpkg/fslib';
// Convert between path types using utilities
const nativePath = '/some/path' as NativePath;
const portablePath = convertPath(ppath, nativePath);
// Result: '/some/path' as PortablePath
const backToNative = convertPath(npath, portablePath);
// Result: '/some/path' as NativePath (or platform-specific format)import { ppath, type PortablePath } from '@yarnpkg/fslib';
// Check if path is contained within another path (security)
function isPathSafe(basePath: PortablePath, targetPath: PortablePath): boolean {
const resolved = ppath.resolve(basePath, targetPath);
return ppath.contains(basePath, resolved);
}
// Usage
const isAllowed = isPathSafe(
'/safe/directory' as PortablePath,
'relative/path/to/file.txt' as PortablePath
);import { ppath, type PortablePath } from '@yarnpkg/fslib';
// Find common ancestor path
function findCommonPath(paths: PortablePath[]): PortablePath {
if (paths.length === 0) return '/' as PortablePath;
let common = paths[0];
for (const path of paths.slice(1)) {
while (!ppath.contains(common, path) && !ppath.contains(path, common)) {
common = ppath.dirname(common);
}
}
return common;
}
// Usage
const commonPath = findCommonPath([
'/project/src/components/Button.tsx' as PortablePath,
'/project/src/utils/helpers.ts' as PortablePath,
'/project/src/types/index.ts' as PortablePath
]);
// Result: '/project/src' as PortablePathimport { xfs, ppath, type PortablePath } from '@yarnpkg/fslib';
// Combine path utilities with filesystem operations
async function processFileTree(basePath: PortablePath): Promise<void> {
const files = await xfs.readdirPromise(basePath, { withFileTypes: true });
for (const file of files) {
const fullPath = ppath.join(basePath, file.name);
if (file.isDirectory()) {
console.log(`Directory: ${ppath.relative(ppath.cwd(), fullPath)}`);
await processFileTree(fullPath);
} else {
const ext = ppath.extname(fullPath);
console.log(`File: ${file.name} (${ext})`);
}
}
}import { npath, ppath, type PortablePath, type NativePath } from '@yarnpkg/fslib';
// Handle paths from different sources
function normalizePath(inputPath: string): PortablePath {
// Assume input might be native or portable
try {
// Try to treat as native path and convert
const asNative = inputPath as NativePath;
return npath.toPortablePath(asNative);
} catch {
// Already portable or invalid
return ppath.normalize(inputPath as PortablePath);
}
}
// Usage with various input formats
const paths = [
'/unix/style/path',
'C:\\Windows\\Style\\Path',
'./relative/path',
'../parent/directory'
];
const normalized = paths.map(normalizePath);Install with Tessl CLI
npx tessl i tessl/npm-yarnpkg--fslib