CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-metro-memory-fs

A memory-based implementation of Node.js fs module for testing purposes

Overall
score

96%

Overview
Eval results
Files

symbolic-links.mddocs/

Symbolic Links

Symbolic link and hard link creation, reading, and path resolution with cross-platform support. Provides complete symlink functionality including link creation, target reading, and real path resolution.

Capabilities

Symbolic Link Creation

Create symbolic links pointing to other files or directories.

/**
 * Synchronously create a symbolic link
 * @param target - Path that the symlink points to (can be relative or absolute)
 * @param path - Path where the symlink will be created
 * @param type - Link type (defaults to 'file', only 'file' is supported)
 */
symlinkSync(target: string | Buffer, path: string | Buffer, type?: string): void;

/**
 * Asynchronously create a symbolic link
 * @param target - Path that the symlink points to
 * @param path - Path where the symlink will be created
 * @param type - Link type (defaults to 'file')
 * @param callback - Completion callback
 */
symlink(target: string | Buffer, path: string | Buffer, type?: string, callback?: (err?: Error) => void): void;
symlink(target: string | Buffer, path: string | Buffer, callback: (err?: Error) => void): void;

Usage Examples:

// Create symlink to a file
fs.writeFileSync('/original.txt', 'Original content');
fs.symlinkSync('/original.txt', '/link-to-original.txt');

// Create symlink with relative target
fs.symlinkSync('../data/config.json', '/app/config.json');

// Create symlink to directory
fs.mkdirSync('/source-dir');
fs.symlinkSync('/source-dir', '/link-to-dir');

// Async symlink creation
fs.symlink('/target/file.txt', '/links/symlink.txt', (err) => {
  if (!err) {
    console.log('Symbolic link created');
  }
});

// Promise-based symlink creation
await fs.promises.symlink('/source', '/destination-link');

Reading Symbolic Links

Read the target path of symbolic links.

/**
 * Synchronously read the target of a symbolic link
 * @param path - Path to the symbolic link
 * @param options - Read options including encoding
 * @returns Target path as string or Buffer
 */
readlinkSync(path: string | Buffer, options?: ReadlinkOptions): string | Buffer;
readlinkSync(path: string | Buffer, encoding: string): string | Buffer;

/**
 * Asynchronously read the target of a symbolic link
 * @param path - Path to the symbolic link
 * @param options - Read options including encoding
 * @param callback - Completion callback with target path
 */
readlink(path: string | Buffer, options?: ReadlinkOptions, callback?: (err?: Error, linkString?: string | Buffer) => void): void;
readlink(path: string | Buffer, encoding: string, callback: (err?: Error, linkString?: string | Buffer) => void): void;
readlink(path: string | Buffer, callback: (err?: Error, linkString?: string) => void): void;

interface ReadlinkOptions {
  /** Encoding for returned path */
  encoding?: string;
}

Usage Examples:

// Create and read a symlink
fs.symlinkSync('/etc/config.conf', '/app/config.conf');
const target = fs.readlinkSync('/app/config.conf');
console.log('Symlink points to:', target); // '/etc/config.conf'

// Read with specific encoding
const targetBuffer = fs.readlinkSync('/app/config.conf', { encoding: 'buffer' });
console.log('Target as buffer:', targetBuffer);

// Read as UTF-8 string
const targetStr = fs.readlinkSync('/app/config.conf', 'utf8');

// Async readlink
fs.readlink('/app/config.conf', (err, target) => {
  if (!err) {
    console.log('Symlink target:', target);
  }
});

// Promise-based readlink
const target = await fs.promises.readlink('/app/config.conf');

Hard Link Creation

Create hard links that reference the same file data.

/**
 * Synchronously create a hard link
 * @param existingPath - Path to existing file
 * @param newPath - Path for the new hard link
 */
linkSync(existingPath: string | Buffer, newPath: string | Buffer): void;

/**
 * Asynchronously create a hard link
 * @param existingPath - Path to existing file
 * @param newPath - Path for the new hard link
 * @param callback - Completion callback
 */
link(existingPath: string | Buffer, newPath: string | Buffer, callback?: (err?: Error) => void): void;

Usage Examples:

// Create original file
fs.writeFileSync('/original.txt', 'Shared content');

// Create hard link
fs.linkSync('/original.txt', '/hardlink.txt');

// Both files share the same content
console.log(fs.readFileSync('/original.txt', 'utf8'));  // 'Shared content'
console.log(fs.readFileSync('/hardlink.txt', 'utf8')); // 'Shared content'

// Modifying one affects the other
fs.writeFileSync('/hardlink.txt', 'Modified content');
console.log(fs.readFileSync('/original.txt', 'utf8')); // 'Modified content'

// Async hard link creation
fs.link('/source.txt', '/another-link.txt', (err) => {
  if (!err) {
    console.log('Hard link created');
  }
});

// Promise-based hard link
await fs.promises.link('/existing.txt', '/new-hardlink.txt');

Real Path Resolution

Resolve absolute paths by following symbolic links.

/**
 * Synchronously resolve a path to its absolute real path
 * @param path - Path to resolve (may contain symlinks)
 * @returns Absolute real path with symlinks resolved
 */
realpathSync(path: string | Buffer): string;

/**
 * Asynchronously resolve a path to its absolute real path
 * @param path - Path to resolve (may contain symlinks)
 * @param callback - Completion callback with resolved path
 */
realpath(path: string | Buffer, callback: (err?: Error, resolvedPath?: string) => void): void;

/**
 * Native realpath implementation (same as realpath)
 */
realpathSync.native(path: string | Buffer): string;
realpath.native(path: string | Buffer, callback: (err?: Error, resolvedPath?: string) => void): void;

Usage Examples:

// Create a chain of symlinks
fs.writeFileSync('/real-file.txt', 'content');
fs.symlinkSync('/real-file.txt', '/link1.txt');
fs.symlinkSync('/link1.txt', '/link2.txt');

// Resolve the real path
const realPath = fs.realpathSync('/link2.txt');
console.log(realPath); // '/real-file.txt'

// Resolve relative paths
fs.symlinkSync('../config/app.json', '/app/config.json');
const resolved = fs.realpathSync('/app/config.json');
console.log(resolved); // Absolute path to the actual file

// Async realpath
fs.realpath('/complex/symlink/path', (err, resolvedPath) => {
  if (!err) {
    console.log('Real path:', resolvedPath);
  }
});

// Promise-based realpath
const realPath = await fs.promises.realpath('/symlink/chain');

// Use native implementation
const nativeRealPath = fs.realpathSync.native('/symlink');

Link Detection and Statistics

Detect symbolic links and get their statistics.

/**
 * Get stats for a path without following symbolic links
 * @param path - Path to get stats for
 * @returns Stats object (symlinks return symlink stats, not target stats)
 */
lstatSync(path: string | Buffer): Stats;

/**
 * Asynchronously get stats without following symbolic links
 * @param path - Path to get stats for
 * @param callback - Completion callback with stats
 */
lstat(path: string | Buffer, callback: (err?: Error, stats?: Stats) => void): void;

interface Stats {
  /** Check if this is a symbolic link */
  isSymbolicLink(): boolean;
  /** Check if this is a regular file */
  isFile(): boolean;
  /** Check if this is a directory */
  isDirectory(): boolean;
  size: number;
  mode: number;
  // ... other properties
}

Usage Examples:

// Create file and symlink
fs.writeFileSync('/target.txt', 'Hello World');
fs.symlinkSync('/target.txt', '/link.txt');

// Compare stat vs lstat
const fileStats = fs.statSync('/link.txt');    // Stats of target file
const linkStats = fs.lstatSync('/link.txt');   // Stats of symlink itself

console.log('File is symlink:', fileStats.isSymbolicLink()); // false
console.log('Link is symlink:', linkStats.isSymbolicLink()); // true

console.log('File size:', fileStats.size);     // 11 (length of "Hello World")
console.log('Link size:', linkStats.size);     // Length of target path string

// Check if path is a symlink
function isSymlink(path) {
  try {
    return fs.lstatSync(path).isSymbolicLink();
  } catch (err) {
    return false;
  }
}

console.log('Is symlink:', isSymlink('/link.txt')); // true

// Async lstat
fs.lstat('/maybe-symlink.txt', (err, stats) => {
  if (!err) {
    if (stats.isSymbolicLink()) {
      console.log('This is a symbolic link');
    } else {
      console.log('This is not a symbolic link');
    }
  }
});

Advanced Link Operations

// Follow symlink chain manually
function followSymlinkChain(path, maxDepth = 10) {
  const chain = [];
  let current = path;
  let depth = 0;
  
  while (depth < maxDepth) {
    chain.push(current);
    
    try {
      const stats = fs.lstatSync(current);
      if (!stats.isSymbolicLink()) {
        break; // Reached the end
      }
      
      current = fs.readlinkSync(current);
      depth++;
    } catch (err) {
      break;
    }
  }
  
  return chain;
}

// Create backup with hard link
function createBackupHardLink(originalPath, backupPath) {
  try {
    fs.linkSync(originalPath, backupPath);
    console.log('Backup created with hard link');
  } catch (err) {
    // Fallback to copy if hard link fails
    fs.copyFileSync(originalPath, backupPath);
    console.log('Backup created with copy');
  }
}

// Safe symlink creation (check if target exists)
function createSafeSymlink(target, linkPath) {
  // Check if target exists
  if (!fs.existsSync(target)) {
    throw new Error(`Target does not exist: ${target}`);
  }
  
  // Remove existing link if present
  if (fs.existsSync(linkPath)) {
    const stats = fs.lstatSync(linkPath);
    if (stats.isSymbolicLink()) {
      fs.unlinkSync(linkPath);
    } else {
      throw new Error(`Cannot create symlink: ${linkPath} already exists and is not a symlink`);
    }
  }
  
  fs.symlinkSync(target, linkPath);
}

Cross-Platform Considerations

// Handle both Windows and POSIX paths in symlinks
function createCrossPlatformSymlink(target, linkPath) {
  // Normalize path separators for the platform
  const normalizedTarget = target.replace(/[/\\]/g, path.sep);
  fs.symlinkSync(normalizedTarget, linkPath);
}

// Resolve symlinks with proper path handling
function resolveWithPlatformPaths(symlinkPath) {
  const realPath = fs.realpathSync(symlinkPath);
  return path.normalize(realPath);
}

Error Handling

Common symbolic link errors:

  • ENOENT - Target file/directory doesn't exist (readlink, realpath)
  • EEXIST - Link path already exists (symlink, link)
  • EINVAL - Path is not a symbolic link (readlink)
  • EPERM - Cannot create hard link to directory
  • ELOOP - Too many symbolic links (realpath)
  • ENOTDIR - Component in path is not a directory

Install with Tessl CLI

npx tessl i tessl/npm-metro-memory-fs

docs

directory-operations.md

file-descriptors.md

file-operations.md

file-watching.md

index.md

stats-permissions.md

streams.md

symbolic-links.md

tile.json