or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-features.mdconstants-and-utilities.mdfilesystem-abstractions.mdfilesystem-implementations.mdindex.mdpath-handling.md

filesystem-abstractions.mddocs/

0

# Filesystem Abstractions

1

2

Core filesystem interfaces and abstract base classes that define the filesystem API and provide the foundation for all filesystem implementations in @yarnpkg/fslib.

3

4

## Overview

5

6

The filesystem abstraction layer provides a unified interface for different filesystem implementations through abstract base classes and comprehensive type definitions. This allows for seamless switching between real filesystems, virtual filesystems, and specialized filesystem implementations.

7

8

## Core Types and Interfaces

9

10

### Basic Types

11

12

```typescript { .api }

13

// Buffer and encoding types

14

type BufferEncodingOrBuffer = BufferEncoding | 'buffer';

15

16

// Enhanced statistics types with optional CRC

17

interface Stats extends fs.Stats {

18

crc?: number;

19

}

20

21

interface BigIntStats extends fs.BigIntStats {

22

crc?: number;

23

}

24

25

// Directory entry types

26

interface Dirent<T extends Path> extends fs.Dirent {

27

name: Filename;

28

path: T;

29

}

30

31

interface DirentNoPath extends fs.Dirent {

32

name: Filename;

33

}

34

35

// Directory handle interface

36

interface Dir<P extends Path> extends AsyncIterable<Dirent<P>> {

37

readonly path: P;

38

read(): Promise<Dirent<P> | null>;

39

close(): Promise<void>;

40

}

41

42

// Symlink types

43

type SymlinkType = 'file' | 'dir' | 'junction';

44

```

45

46

### Options Types

47

48

```typescript { .api }

49

// Directory operations

50

interface OpendirOptions {

51

bufferSize?: number;

52

}

53

54

interface ReaddirOptions {

55

encoding?: BufferEncoding;

56

withFileTypes?: boolean;

57

}

58

59

interface MkdirOptions {

60

mode?: number;

61

recursive?: boolean;

62

}

63

64

interface RmdirOptions {

65

maxRetries?: number;

66

recursive?: boolean;

67

retryDelay?: number;

68

}

69

70

interface RmOptions {

71

force?: boolean;

72

maxRetries?: number;

73

recursive?: boolean;

74

retryDelay?: number;

75

}

76

77

// File operations

78

interface WriteFileOptions {

79

encoding?: BufferEncoding;

80

mode?: number;

81

flag?: string;

82

}

83

84

interface StatOptions {

85

bigint?: false;

86

}

87

88

interface StatSyncOptions {

89

bigint?: boolean;

90

throwIfNoEntry?: boolean;

91

}

92

93

// Stream operations

94

interface CreateReadStreamOptions {

95

encoding?: BufferEncoding;

96

fd?: number;

97

flags?: string;

98

mode?: number;

99

autoClose?: boolean;

100

emitClose?: boolean;

101

start?: number;

102

end?: number;

103

highWaterMark?: number;

104

}

105

106

interface CreateWriteStreamOptions {

107

encoding?: BufferEncoding;

108

fd?: number;

109

flags?: string;

110

mode?: number;

111

autoClose?: boolean;

112

emitClose?: boolean;

113

start?: number;

114

highWaterMark?: number;

115

}

116

117

// Watch operations

118

interface WatchOptions {

119

encoding?: BufferEncoding;

120

persistent?: boolean;

121

recursive?: boolean;

122

}

123

124

interface WatchFileOptions {

125

bigint?: boolean;

126

persistent?: boolean;

127

interval?: number;

128

}

129

130

interface ExtractHintOptions {

131

relevantExtensions?: Set<string>;

132

}

133

```

134

135

### Callback Types

136

137

```typescript { .api }

138

// Watch callback types

139

type WatchCallback = (eventType: string, filename: string | null) => void;

140

141

type WatchFileCallback = (current: Stats, previous: Stats) => void;

142

143

// Watcher interfaces

144

interface Watcher extends EventEmitter {

145

close(): void;

146

}

147

148

interface StatWatcher extends EventEmitter {

149

ref(): StatWatcher;

150

unref(): StatWatcher;

151

}

152

```

153

154

## Abstract Base Classes

155

156

### FakeFS<P extends Path>

157

158

The core abstract base class that defines the complete filesystem interface.

159

160

```typescript { .api }

161

import { FakeFS, type Path, type Stats, type Dirent } from '@yarnpkg/fslib';

162

163

abstract class FakeFS<P extends Path> {

164

// File reading operations

165

abstract readFilePromise(p: FSPath<P>, encoding?: null): Promise<Buffer>;

166

abstract readFilePromise(p: FSPath<P>, encoding: BufferEncoding): Promise<string>;

167

abstract readFilePromise(p: FSPath<P>, encoding?: BufferEncoding | null): Promise<Buffer | string>;

168

169

abstract readFileSync(p: FSPath<P>, encoding?: null): Buffer;

170

abstract readFileSync(p: FSPath<P>, encoding: BufferEncoding): string;

171

abstract readFileSync(p: FSPath<P>, encoding?: BufferEncoding | null): Buffer | string;

172

173

// File writing operations

174

abstract writeFilePromise(p: P, content: string | Buffer, options?: WriteFileOptions): Promise<void>;

175

abstract writeFileSync(p: P, content: string | Buffer, options?: WriteFileOptions): void;

176

177

abstract appendFilePromise(p: P, content: string | Buffer, options?: WriteFileOptions): Promise<void>;

178

abstract appendFileSync(p: P, content: string | Buffer, options?: WriteFileOptions): void;

179

180

// Directory operations

181

abstract mkdirPromise(p: P, options?: MkdirOptions): Promise<void>;

182

abstract mkdirSync(p: P, options?: MkdirOptions): void;

183

184

abstract readdirPromise(p: P): Promise<Filename[]>;

185

abstract readdirPromise(p: P, options: ReaddirOptions & { withFileTypes: true }): Promise<Dirent<P>[]>;

186

abstract readdirPromise(p: P, options?: ReaddirOptions): Promise<Filename[] | Dirent<P>[]>;

187

188

abstract readdirSync(p: P): Filename[];

189

abstract readdirSync(p: P, options: ReaddirOptions & { withFileTypes: true }): Dirent<P>[];

190

abstract readdirSync(p: P, options?: ReaddirOptions): Filename[] | Dirent<P>[];

191

192

// Metadata operations

193

abstract statPromise(p: FSPath<P>): Promise<Stats>;

194

abstract statPromise(p: FSPath<P>, options: StatOptions & { bigint: true }): Promise<BigIntStats>;

195

abstract statPromise(p: FSPath<P>, options?: StatOptions): Promise<Stats | BigIntStats>;

196

197

abstract statSync(p: FSPath<P>): Stats;

198

abstract statSync(p: FSPath<P>, options: StatSyncOptions & { bigint: true }): BigIntStats;

199

abstract statSync(p: FSPath<P>, options?: StatSyncOptions): Stats | BigIntStats;

200

201

abstract lstatPromise(p: P): Promise<Stats>;

202

abstract lstatPromise(p: P, options: StatOptions & { bigint: true }): Promise<BigIntStats>;

203

abstract lstatPromise(p: P, options?: StatOptions): Promise<Stats | BigIntStats>;

204

205

abstract lstatSync(p: P): Stats;

206

abstract lstatSync(p: P, options: StatSyncOptions & { bigint: true }): BigIntStats;

207

abstract lstatSync(p: P, options?: StatSyncOptions): Stats | BigIntStats;

208

209

// Permission and ownership

210

abstract chmodPromise(p: P, mode: number): Promise<void>;

211

abstract chmodSync(p: P, mode: number): void;

212

213

abstract chownPromise(p: P, uid: number, gid: number): Promise<void>;

214

abstract chownSync(p: P, uid: number, gid: number): void;

215

216

// File system operations

217

abstract copyFilePromise(source: P, destination: P, flags?: number): Promise<void>;

218

abstract copyFileSync(source: P, destination: P, flags?: number): void;

219

220

abstract renamePromise(oldPath: P, newPath: P): Promise<void>;

221

abstract renameSync(oldPath: P, newPath: P): void;

222

223

abstract unlinkPromise(p: P): Promise<void>;

224

abstract unlinkSync(p: P): void;

225

226

// Link operations

227

abstract linkPromise(existingPath: P, newPath: P): Promise<void>;

228

abstract linkSync(existingPath: P, newPath: P): void;

229

230

abstract symlinkPromise(target: P, p: P, type?: SymlinkType): Promise<void>;

231

abstract symlinkSync(target: P, p: P, type?: SymlinkType): void;

232

233

abstract readlinkPromise(p: P): Promise<P>;

234

abstract readlinkSync(p: P): P;

235

236

// Directory handle operations

237

abstract opendirPromise(p: P, options?: OpendirOptions): Promise<Dir<P>>;

238

abstract opendirSync(p: P, options?: OpendirOptions): Dir<P>;

239

240

// Stream operations

241

abstract createReadStream(p: P, options?: CreateReadStreamOptions): fs.ReadStream;

242

abstract createWriteStream(p: P, options?: CreateWriteStreamOptions): fs.WriteStream;

243

244

// Watch operations

245

abstract watchPromise(p: P, options?: WatchOptions): Promise<AsyncIterable<string>>;

246

abstract watch(p: P, options?: WatchOptions): Watcher;

247

abstract watch(p: P, options: WatchOptions, callback: WatchCallback): Watcher;

248

abstract watch(p: P, callback: WatchCallback): Watcher;

249

}

250

```

251

252

### BasePortableFakeFS

253

254

Specialized abstract base class for filesystems that use portable paths.

255

256

```typescript { .api }

257

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

258

259

abstract class BasePortableFakeFS extends FakeFS<PortablePath> {

260

// Inherits all FakeFS methods with PortablePath as the path type

261

// Additional portable path specific utilities may be added here

262

}

263

```

264

265

## Utility Functions

266

267

### Line Ending Normalization

268

269

```typescript { .api }

270

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

271

272

// Normalize line endings based on original content style

273

function normalizeLineEndings(originalContent: string, newContent: string): string;

274

275

// Usage example

276

const original = "line1\nline2\nline3"; // Unix line endings

277

const modified = "line1\r\nmodified\r\nline3"; // Windows line endings

278

const normalized = normalizeLineEndings(original, modified);

279

// Result: "line1\nmodified\nline3" (matches original style)

280

```

281

282

## Usage Patterns

283

284

### Implementing a Custom Filesystem

285

286

```typescript { .api }

287

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

288

289

class CustomFileSystem extends BasePortableFakeFS {

290

private data = new Map<string, Buffer>();

291

292

async readFilePromise(p: PortablePath, encoding?: BufferEncoding | null): Promise<Buffer | string> {

293

const content = this.data.get(p);

294

if (!content) {

295

throw new Error(`File not found: ${p}`);

296

}

297

298

return encoding && encoding !== 'buffer' ? content.toString(encoding) : content;

299

}

300

301

readFileSync(p: PortablePath, encoding?: BufferEncoding | null): Buffer | string {

302

// Synchronous implementation

303

const content = this.data.get(p);

304

if (!content) {

305

throw new Error(`File not found: ${p}`);

306

}

307

308

return encoding && encoding !== 'buffer' ? content.toString(encoding) : content;

309

}

310

311

async writeFilePromise(p: PortablePath, content: string | Buffer): Promise<void> {

312

const buffer = Buffer.isBuffer(content) ? content : Buffer.from(content);

313

this.data.set(p, buffer);

314

}

315

316

writeFileSync(p: PortablePath, content: string | Buffer): void {

317

const buffer = Buffer.isBuffer(content) ? content : Buffer.from(content);

318

this.data.set(p, buffer);

319

}

320

321

// Implement other required methods...

322

}

323

```

324

325

### Working with Directory Entries

326

327

```typescript { .api }

328

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

329

330

async function processDirectoryEntries(fs: FakeFS<PortablePath>, dir: PortablePath): Promise<void> {

331

const entries = await fs.readdirPromise(dir, { withFileTypes: true });

332

333

for (const entry of entries) {

334

console.log(`Processing: ${entry.name} (${entry.isDirectory() ? 'dir' : 'file'})`);

335

336

if (entry.isDirectory()) {

337

// Recursively process subdirectory

338

const subPath = ppath.join(dir, entry.name);

339

await processDirectoryEntries(fs, subPath);

340

} else if (entry.isFile()) {

341

// Process file

342

const filePath = ppath.join(dir, entry.name);

343

const content = await fs.readFilePromise(filePath, 'utf8');

344

console.log(`File size: ${content.length} characters`);

345

}

346

}

347

}

348

```

349

350

### Stream Operations

351

352

```typescript { .api }

353

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

354

import { pipeline } from 'stream/promises';

355

356

async function copyFileWithStreams(

357

fs: FakeFS<PortablePath>,

358

source: PortablePath,

359

destination: PortablePath

360

): Promise<void> {

361

const readStream = fs.createReadStream(source);

362

const writeStream = fs.createWriteStream(destination);

363

364

await pipeline(readStream, writeStream);

365

}

366

367

// Usage with transform streams

368

import { Transform } from 'stream';

369

370

async function processFileContent(

371

fs: FakeFS<PortablePath>,

372

inputPath: PortablePath,

373

outputPath: PortablePath,

374

transformer: Transform

375

): Promise<void> {

376

await pipeline(

377

fs.createReadStream(inputPath),

378

transformer,

379

fs.createWriteStream(outputPath)

380

);

381

}

382

```

383

384

### Watch Operations

385

386

```typescript { .api }

387

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

388

389

function setupFileWatcher(fs: FakeFS<PortablePath>, path: PortablePath): void {

390

const callback: WatchCallback = (eventType, filename) => {

391

console.log(`File ${filename} changed: ${eventType}`);

392

};

393

394

const watcher = fs.watch(path, { recursive: true }, callback);

395

396

// Clean up after 10 seconds

397

setTimeout(() => {

398

watcher.close();

399

}, 10000);

400

}

401

402

// Async watch with iteration

403

async function watchDirectory(fs: FakeFS<PortablePath>, path: PortablePath): Promise<void> {

404

const watcher = await fs.watchPromise(path, { recursive: true });

405

406

for await (const event of watcher) {

407

console.log(`Directory change detected: ${event}`);

408

// Handle the event

409

}

410

}

411

```

412

413

## Error Handling

414

415

### Common Filesystem Errors

416

417

```typescript { .api }

418

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

419

420

async function safeFileOperation(fs: FakeFS<PortablePath>, path: PortablePath): Promise<string | null> {

421

try {

422

return await fs.readFilePromise(path, 'utf8');

423

} catch (error) {

424

if (error.code === 'ENOENT') {

425

// File doesn't exist

426

return null;

427

} else if (error.code === 'EACCES') {

428

// Permission denied

429

throw errors.EACCES(`Cannot access file: ${path}`);

430

} else {

431

// Re-throw other errors

432

throw error;

433

}

434

}

435

}

436

437

// Check file existence safely

438

async function fileExists(fs: FakeFS<PortablePath>, path: PortablePath): Promise<boolean> {

439

try {

440

await fs.statPromise(path);

441

return true;

442

} catch (error) {

443

if (error.code === 'ENOENT') {

444

return false;

445

}

446

throw error;

447

}

448

}

449

```

450

451

## Related Documentation

452

453

- [Path Handling](./path-handling.md) - Path utilities used by filesystem operations

454

- [Filesystem Implementations](./filesystem-implementations.md) - Concrete implementations of these abstractions

455

- [Constants and Utilities](./constants-and-utilities.md) - Error factories and statistics utilities

456

- [Advanced Features](./advanced-features.md) - Advanced operations building on these abstractions