CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-yarnpkg--fslib

A TypeScript library abstracting the Node filesystem APIs with virtual filesystems and cross-platform path handling

Overview
Eval results
Files

constants-and-utilities.mddocs/

Constants and Utilities

File mode constants, error factories, and statistics utilities that provide essential building blocks for filesystem operations.

Overview

The @yarnpkg/fslib package provides three key utility modules:

  • constants: File mode and path constants for filesystem operations
  • errors: Factory functions for creating filesystem-specific errors
  • statUtils: Utilities for working with file statistics and metadata

Constants

File Mode Constants

Standard POSIX file mode constants for working with file types and permissions.

import { constants } from '@yarnpkg/fslib';

// File type mask and specific types
const S_IFMT = constants.S_IFMT;     // 0o170000 - File type mask
const S_IFDIR = constants.S_IFDIR;   // 0o040000 - Directory
const S_IFREG = constants.S_IFREG;   // 0o100000 - Regular file
const S_IFLNK = constants.S_IFLNK;   // 0o120000 - Symbolic link

// Safe timestamp constant
const SAFE_TIME = constants.SAFE_TIME; // 456789000 (1984-06-22T21:50:00.000Z)

Working with File Mode Constants

import { constants, type Stats } from '@yarnpkg/fslib';

// Check file type from stats
function getFileType(stats: Stats): string {
  const mode = stats.mode;
  
  if ((mode & constants.S_IFMT) === constants.S_IFDIR) {
    return 'directory';
  } else if ((mode & constants.S_IFMT) === constants.S_IFREG) {
    return 'file';
  } else if ((mode & constants.S_IFMT) === constants.S_IFLNK) {
    return 'symlink';
  } else {
    return 'other';
  }
}

// Usage with filesystem operations
import { xfs } from '@yarnpkg/fslib';

const stats = await xfs.statPromise('/path/to/item' as PortablePath);
const type = getFileType(stats);
console.log(`Item type: ${type}`);

// Check if item is a directory
const isDirectory = (stats.mode & constants.S_IFMT) === constants.S_IFDIR;

Path Constants

Predefined portable path and filename constants for common filesystem items.

import { PortablePath, Filename } from '@yarnpkg/fslib';

// Portable path constants
const root = PortablePath.root;       // '/' as PortablePath
const dot = PortablePath.dot;         // '.' as PortablePath  
const parent = PortablePath.parent;   // '..' as PortablePath

// Common filename constants
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
const env = Filename.env;                 // '.env' as Filename

// Yarn-specific constants
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

Using Path Constants

import { ppath, PortablePath, Filename } from '@yarnpkg/fslib';

// Build common paths
const projectRoot = '/my/project' as PortablePath;
const nodeModulesPath = ppath.join(projectRoot, Filename.nodeModules);
const packageJsonPath = ppath.join(projectRoot, Filename.manifest);
const yarnLockPath = ppath.join(projectRoot, Filename.lockfile);

// Work with Yarn-specific paths
const virtualPath = ppath.join(nodeModulesPath, Filename.virtual);
const pnpPath = ppath.join(projectRoot, Filename.pnpCjs);

// Navigate using path constants
const parentDir = ppath.join(projectRoot, PortablePath.parent);
const currentDir = ppath.join(projectRoot, PortablePath.dot);

Error Utilities

Factory functions for creating filesystem-specific errors with appropriate error codes and messages.

Basic Error Factories

import { errors } from '@yarnpkg/fslib';

// File and directory errors
const busyError = errors.EBUSY('Resource is busy');
const notFoundError = errors.ENOENT('File not found: /missing/file.txt');
const notDirError = errors.ENOTDIR('Not a directory: /file.txt');
const isDirError = errors.EISDIR('Is a directory: /some/directory');
const existsError = errors.EEXIST('File already exists: /existing/file.txt');

// Permission and access errors  
const accessError = errors.EBADF('Bad file descriptor');
const invalidError = errors.EINVAL('Invalid argument provided');
const readOnlyError = errors.EROFS('Read-only filesystem');
const notEmptyError = errors.ENOTEMPTY('Directory not empty: /directory');

// Operation errors
const notSupportedError = errors.EOPNOTSUPP('Operation not supported');
const notImplementedError = errors.ENOSYS('Function not implemented', 'custom reason');
const dirClosedError = errors.ERR_DIR_CLOSED();

Using Error Factories

import { errors, type FakeFS, type PortablePath } from '@yarnpkg/fslib';

// Custom filesystem implementation with proper error handling
class CustomFS extends BasePortableFakeFS {
  private files = new Map<string, Buffer>();
  
  async readFilePromise(p: PortablePath, encoding?: BufferEncoding | null): Promise<Buffer | string> {
    if (!this.files.has(p)) {
      throw errors.ENOENT(`No such file: ${p}`);
    }
    
    const content = this.files.get(p)!;
    return encoding && encoding !== 'buffer' ? content.toString(encoding) : content;
  }
  
  async writeFilePromise(p: PortablePath, content: string | Buffer): Promise<void> {
    if (this.isReadOnly) {
      throw errors.EROFS(`Cannot write to read-only filesystem: ${p}`);
    }
    
    const buffer = Buffer.isBuffer(content) ? content : Buffer.from(content);
    this.files.set(p, buffer);
  }
  
  async mkdirPromise(p: PortablePath, options?: MkdirOptions): Promise<void> {
    if (this.files.has(p)) {
      throw errors.EEXIST(`Directory already exists: ${p}`);
    }
    
    if (!options?.recursive) {
      const parent = ppath.dirname(p);
      if (parent !== p && !this.files.has(parent)) {
        throw errors.ENOENT(`Parent directory does not exist: ${parent}`);
      }
    }
    
    this.files.set(p, Buffer.alloc(0)); // Mark as directory
  }
}

Error Code Handling

import { errors, xfs, type PortablePath } from '@yarnpkg/fslib';

// Robust error handling in filesystem operations
async function safeFileRead(path: PortablePath): Promise<string | null> {
  try {
    return await xfs.readFilePromise(path, 'utf8');
  } catch (error) {
    switch (error.code) {
      case 'ENOENT':
        console.log(`File not found: ${path}`);
        return null;
        
      case 'EACCES':
        console.error(`Access denied: ${path}`);
        throw errors.EACCES(`Cannot read file: ${path}`);
        
      case 'EISDIR':
        console.error(`Expected file but found directory: ${path}`);
        throw errors.EISDIR(`Cannot read directory as file: ${path}`);
        
      default:
        console.error(`Unexpected error reading ${path}:`, error.message);
        throw error;
    }
  }
}

// Conditional file operations based on error types
async function ensureFileExists(path: PortablePath, defaultContent: string): Promise<void> {
  try {
    await xfs.statPromise(path);
    // File exists, nothing to do
  } catch (error) {
    if (error.code === 'ENOENT') {
      // File doesn't exist, create it
      await xfs.writeFilePromise(path, defaultContent);
    } else {
      // Other error, re-throw
      throw error;
    }
  }
}

Statistics Utilities

Utilities for working with file statistics, creating default stat objects, and comparing file metadata.

Statistics Classes

import { statUtils, type Stats, type BigIntStats, type Dirent } from '@yarnpkg/fslib';

// Directory entry with filesystem type information
class DirEntry<T extends Path> implements Dirent<T> {
  constructor(
    public name: Filename,
    public path: T,
    private stats: Stats
  ) {}
  
  isFile(): boolean { return this.stats.isFile(); }
  isDirectory(): boolean { return this.stats.isDirectory(); }
  isBlockDevice(): boolean { return this.stats.isBlockDevice(); }
  isCharacterDevice(): boolean { return this.stats.isCharacterDevice(); }
  isSymbolicLink(): boolean { return this.stats.isSymbolicLink(); }
  isFIFO(): boolean { return this.stats.isFIFO(); }
  isSocket(): boolean { return this.stats.isSocket(); }
}

// File statistics entry (extends Node.js Stats)
class StatEntry implements Stats {
  dev: number;
  ino: number;
  mode: number;
  nlink: number;
  uid: number;
  gid: number;
  rdev: number;
  size: number;
  blksize: number;
  blocks: number;
  atimeMs: number;
  mtimeMs: number;
  ctimeMs: number;
  birthtimeMs: number;
  atime: Date;
  mtime: Date;
  ctime: Date;
  birthtime: Date;
  crc?: number; // Optional CRC checksum
  
  // Standard Stats methods...
}

Statistics Constants and Utilities

import { statUtils, constants } from '@yarnpkg/fslib';

// Default file mode
const DEFAULT_MODE = statUtils.DEFAULT_MODE; // S_IFREG | 0o644

// Create default statistics objects
const defaultStats = statUtils.makeDefaultStats();
const emptyStats = statUtils.makeEmptyStats();

// Working with stats objects
const stats1 = statUtils.makeDefaultStats();
stats1.size = 1024;
stats1.mode = constants.S_IFREG | 0o755;

const stats2 = statUtils.makeDefaultStats(); 
stats2.size = 1024;
stats2.mode = constants.S_IFREG | 0o755;

// Compare statistics objects
const areEqual = statUtils.areStatsEqual(stats1, stats2);
console.log('Stats are equal:', areEqual);

// Clear statistics (mutates object)
statUtils.clearStats(stats1);
console.log('Cleared size:', stats1.size); // 0

Statistics Conversion

import { statUtils, type Stats, type BigIntStats } from '@yarnpkg/fslib';

// Convert regular Stats to BigIntStats
const regularStats = statUtils.makeDefaultStats();
regularStats.size = 1024;
regularStats.mtimeMs = Date.now();

const bigIntStats: BigIntStats = statUtils.convertToBigIntStats(regularStats);
console.log('BigInt size:', bigIntStats.size); // 1024n
console.log('BigInt mtime:', bigIntStats.mtimeMs); // BigInt version

Creating Custom Statistics

import { statUtils, constants, type Stats } from '@yarnpkg/fslib';

// Create file statistics
function createFileStats(size: number, mtime: Date): Stats {
  const stats = statUtils.makeDefaultStats();
  
  stats.size = size;
  stats.mode = constants.S_IFREG | 0o644; // Regular file, rw-r--r--
  stats.mtime = mtime;
  stats.mtimeMs = mtime.getTime();
  stats.atime = mtime;
  stats.atimeMs = mtime.getTime();
  stats.ctime = mtime;  
  stats.ctimeMs = mtime.getTime();
  
  return stats;
}

// Create directory statistics
function createDirectoryStats(mtime: Date): Stats {
  const stats = statUtils.makeDefaultStats();
  
  stats.size = 0;
  stats.mode = constants.S_IFDIR | 0o755; // Directory, rwxr-xr-x
  stats.mtime = mtime;
  stats.mtimeMs = mtime.getTime();
  stats.atime = mtime;
  stats.atimeMs = mtime.getTime();
  stats.ctime = mtime;
  stats.ctimeMs = mtime.getTime();
  
  return stats;
}

// Usage
const fileStats = createFileStats(2048, new Date());
const dirStats = createDirectoryStats(new Date());

console.log('Is file:', fileStats.isFile());       // true
console.log('Is directory:', dirStats.isDirectory()); // true

Integration Examples

Comprehensive File Operations

import { 
  constants, errors, statUtils, xfs, ppath,
  type PortablePath, type Stats 
} from '@yarnpkg/fslib';

// Enhanced file information utility
async function getFileInfo(path: PortablePath): Promise<{
  path: PortablePath;
  type: string;
  size: number;
  mode: string;
  exists: boolean;
  readable: boolean;
  writable: boolean;
}> {
  try {
    const stats = await xfs.statPromise(path);
    
    // Determine file type using constants
    let type: string;
    if ((stats.mode & constants.S_IFMT) === constants.S_IFDIR) {
      type = 'directory';
    } else if ((stats.mode & constants.S_IFMT) === constants.S_IFREG) {
      type = 'file';
    } else if ((stats.mode & constants.S_IFMT) === constants.S_IFLNK) {
      type = 'symlink';  
    } else {
      type = 'other';
    }
    
    // Format mode as octal string
    const mode = (stats.mode & 0o777).toString(8).padStart(3, '0');
    
    // Test access permissions
    let readable = false;
    let writable = false;
    
    try {
      await xfs.accessPromise(path, constants.R_OK);
      readable = true;
    } catch {}
    
    try {
      await xfs.accessPromise(path, constants.W_OK);
      writable = true;
    } catch {}
    
    return {
      path,
      type,
      size: stats.size,
      mode,
      exists: true,
      readable,
      writable
    };
    
  } catch (error) {
    if (error.code === 'ENOENT') {
      return {
        path,
        type: 'none',
        size: 0,
        mode: '000',
        exists: false,
        readable: false,
        writable: false
      };
    }
    throw error;
  }
}

// Usage
const info = await getFileInfo('/path/to/file.txt' as PortablePath);
console.log(`${info.path}: ${info.type} (${info.mode}) ${info.size} bytes`);

Safe Timestamp Operations

import { constants, statUtils, type Stats } from '@yarnpkg/fslib';

// Use safe timestamp for reproducible builds
function createReproducibleStats(size: number): Stats {
  const stats = statUtils.makeDefaultStats();
  
  stats.size = size;
  stats.mode = constants.S_IFREG | 0o644;
  
  // Use safe timestamp for reproducibility
  const safeTime = new Date(constants.SAFE_TIME * 1000);
  stats.mtime = safeTime;
  stats.mtimeMs = constants.SAFE_TIME * 1000;
  stats.atime = safeTime;
  stats.atimeMs = constants.SAFE_TIME * 1000;
  stats.ctime = safeTime;
  stats.ctimeMs = constants.SAFE_TIME * 1000;
  
  return stats;
}

// Compare file timestamps safely
function compareFileTimestamps(stats1: Stats, stats2: Stats): number {
  const time1 = Math.floor(stats1.mtimeMs / 1000);
  const time2 = Math.floor(stats2.mtimeMs / 1000);
  
  if (time1 < time2) return -1;
  if (time1 > time2) return 1;
  return 0;
}

Error Context Enhancement

import { errors, type PortablePath } from '@yarnpkg/fslib';

// Enhanced error factory with context
class FileSystemError extends Error {
  constructor(
    public code: string,
    message: string,
    public path?: PortablePath,
    public operation?: string
  ) {
    super(message);
    this.name = 'FileSystemError';
  }
}

// Create contextual errors
function createContextualError(
  operation: string, 
  path: PortablePath, 
  originalError: Error
): FileSystemError {
  const message = `${operation} failed for ${path}: ${originalError.message}`;
  return new FileSystemError(originalError.code || 'UNKNOWN', message, path, operation);
}

// Usage in filesystem operations
async function safeOperation<T>(
  operation: string,
  path: PortablePath,
  fn: () => Promise<T>
): Promise<T> {
  try {
    return await fn();
  } catch (error) {
    throw createContextualError(operation, path, error);
  }
}

// Example usage
try {
  await safeOperation('read', '/missing/file.txt' as PortablePath, async () => {
    return await xfs.readFilePromise('/missing/file.txt' as PortablePath, 'utf8');
  });
} catch (error) {
  console.error(`Operation failed: ${error.operation} on ${error.path}`);
  console.error(`Error: ${error.message}`);
}

Related Documentation

  • Filesystem Abstractions - Base classes that use these constants and utilities
  • Filesystem Implementations - Concrete implementations using error handling
  • Path Handling - Path constants and utilities
  • Advanced Features - Advanced operations building on these foundations

Install with Tessl CLI

npx tessl i tessl/npm-yarnpkg--fslib

docs

advanced-features.md

constants-and-utilities.md

filesystem-abstractions.md

filesystem-implementations.md

index.md

path-handling.md

tile.json