0
# Constants and Utilities
1
2
File mode constants, error factories, and statistics utilities that provide essential building blocks for filesystem operations.
3
4
## Overview
5
6
The @yarnpkg/fslib package provides three key utility modules:
7
- **constants**: File mode and path constants for filesystem operations
8
- **errors**: Factory functions for creating filesystem-specific errors
9
- **statUtils**: Utilities for working with file statistics and metadata
10
11
## Constants
12
13
### File Mode Constants
14
15
Standard POSIX file mode constants for working with file types and permissions.
16
17
```typescript { .api }
18
import { constants } from '@yarnpkg/fslib';
19
20
// File type mask and specific types
21
const S_IFMT = constants.S_IFMT; // 0o170000 - File type mask
22
const S_IFDIR = constants.S_IFDIR; // 0o040000 - Directory
23
const S_IFREG = constants.S_IFREG; // 0o100000 - Regular file
24
const S_IFLNK = constants.S_IFLNK; // 0o120000 - Symbolic link
25
26
// Safe timestamp constant
27
const SAFE_TIME = constants.SAFE_TIME; // 456789000 (1984-06-22T21:50:00.000Z)
28
```
29
30
#### Working with File Mode Constants
31
32
```typescript { .api }
33
import { constants, type Stats } from '@yarnpkg/fslib';
34
35
// Check file type from stats
36
function getFileType(stats: Stats): string {
37
const mode = stats.mode;
38
39
if ((mode & constants.S_IFMT) === constants.S_IFDIR) {
40
return 'directory';
41
} else if ((mode & constants.S_IFMT) === constants.S_IFREG) {
42
return 'file';
43
} else if ((mode & constants.S_IFMT) === constants.S_IFLNK) {
44
return 'symlink';
45
} else {
46
return 'other';
47
}
48
}
49
50
// Usage with filesystem operations
51
import { xfs } from '@yarnpkg/fslib';
52
53
const stats = await xfs.statPromise('/path/to/item' as PortablePath);
54
const type = getFileType(stats);
55
console.log(`Item type: ${type}`);
56
57
// Check if item is a directory
58
const isDirectory = (stats.mode & constants.S_IFMT) === constants.S_IFDIR;
59
```
60
61
### Path Constants
62
63
Predefined portable path and filename constants for common filesystem items.
64
65
```typescript { .api }
66
import { PortablePath, Filename } from '@yarnpkg/fslib';
67
68
// Portable path constants
69
const root = PortablePath.root; // '/' as PortablePath
70
const dot = PortablePath.dot; // '.' as PortablePath
71
const parent = PortablePath.parent; // '..' as PortablePath
72
73
// Common filename constants
74
const home = Filename.home; // '~' as Filename
75
const nodeModules = Filename.nodeModules; // 'node_modules' as Filename
76
const packageJson = Filename.manifest; // 'package.json' as Filename
77
const yarnLock = Filename.lockfile; // 'yarn.lock' as Filename
78
const env = Filename.env; // '.env' as Filename
79
80
// Yarn-specific constants
81
const virtual = Filename.virtual; // '__virtual__' as Filename
82
const pnpCjs = Filename.pnpCjs; // '.pnp.cjs' as Filename
83
const pnpData = Filename.pnpData; // '.pnp.data.json' as Filename
84
const pnpLoader = Filename.pnpEsmLoader; // '.pnp.loader.mjs' as Filename
85
const yarnrc = Filename.rc; // '.yarnrc.yml' as Filename
86
```
87
88
#### Using Path Constants
89
90
```typescript { .api }
91
import { ppath, PortablePath, Filename } from '@yarnpkg/fslib';
92
93
// Build common paths
94
const projectRoot = '/my/project' as PortablePath;
95
const nodeModulesPath = ppath.join(projectRoot, Filename.nodeModules);
96
const packageJsonPath = ppath.join(projectRoot, Filename.manifest);
97
const yarnLockPath = ppath.join(projectRoot, Filename.lockfile);
98
99
// Work with Yarn-specific paths
100
const virtualPath = ppath.join(nodeModulesPath, Filename.virtual);
101
const pnpPath = ppath.join(projectRoot, Filename.pnpCjs);
102
103
// Navigate using path constants
104
const parentDir = ppath.join(projectRoot, PortablePath.parent);
105
const currentDir = ppath.join(projectRoot, PortablePath.dot);
106
```
107
108
## Error Utilities
109
110
Factory functions for creating filesystem-specific errors with appropriate error codes and messages.
111
112
### Basic Error Factories
113
114
```typescript { .api }
115
import { errors } from '@yarnpkg/fslib';
116
117
// File and directory errors
118
const busyError = errors.EBUSY('Resource is busy');
119
const notFoundError = errors.ENOENT('File not found: /missing/file.txt');
120
const notDirError = errors.ENOTDIR('Not a directory: /file.txt');
121
const isDirError = errors.EISDIR('Is a directory: /some/directory');
122
const existsError = errors.EEXIST('File already exists: /existing/file.txt');
123
124
// Permission and access errors
125
const accessError = errors.EBADF('Bad file descriptor');
126
const invalidError = errors.EINVAL('Invalid argument provided');
127
const readOnlyError = errors.EROFS('Read-only filesystem');
128
const notEmptyError = errors.ENOTEMPTY('Directory not empty: /directory');
129
130
// Operation errors
131
const notSupportedError = errors.EOPNOTSUPP('Operation not supported');
132
const notImplementedError = errors.ENOSYS('Function not implemented', 'custom reason');
133
const dirClosedError = errors.ERR_DIR_CLOSED();
134
```
135
136
### Using Error Factories
137
138
```typescript { .api }
139
import { errors, type FakeFS, type PortablePath } from '@yarnpkg/fslib';
140
141
// Custom filesystem implementation with proper error handling
142
class CustomFS extends BasePortableFakeFS {
143
private files = new Map<string, Buffer>();
144
145
async readFilePromise(p: PortablePath, encoding?: BufferEncoding | null): Promise<Buffer | string> {
146
if (!this.files.has(p)) {
147
throw errors.ENOENT(`No such file: ${p}`);
148
}
149
150
const content = this.files.get(p)!;
151
return encoding && encoding !== 'buffer' ? content.toString(encoding) : content;
152
}
153
154
async writeFilePromise(p: PortablePath, content: string | Buffer): Promise<void> {
155
if (this.isReadOnly) {
156
throw errors.EROFS(`Cannot write to read-only filesystem: ${p}`);
157
}
158
159
const buffer = Buffer.isBuffer(content) ? content : Buffer.from(content);
160
this.files.set(p, buffer);
161
}
162
163
async mkdirPromise(p: PortablePath, options?: MkdirOptions): Promise<void> {
164
if (this.files.has(p)) {
165
throw errors.EEXIST(`Directory already exists: ${p}`);
166
}
167
168
if (!options?.recursive) {
169
const parent = ppath.dirname(p);
170
if (parent !== p && !this.files.has(parent)) {
171
throw errors.ENOENT(`Parent directory does not exist: ${parent}`);
172
}
173
}
174
175
this.files.set(p, Buffer.alloc(0)); // Mark as directory
176
}
177
}
178
```
179
180
### Error Code Handling
181
182
```typescript { .api }
183
import { errors, xfs, type PortablePath } from '@yarnpkg/fslib';
184
185
// Robust error handling in filesystem operations
186
async function safeFileRead(path: PortablePath): Promise<string | null> {
187
try {
188
return await xfs.readFilePromise(path, 'utf8');
189
} catch (error) {
190
switch (error.code) {
191
case 'ENOENT':
192
console.log(`File not found: ${path}`);
193
return null;
194
195
case 'EACCES':
196
console.error(`Access denied: ${path}`);
197
throw errors.EACCES(`Cannot read file: ${path}`);
198
199
case 'EISDIR':
200
console.error(`Expected file but found directory: ${path}`);
201
throw errors.EISDIR(`Cannot read directory as file: ${path}`);
202
203
default:
204
console.error(`Unexpected error reading ${path}:`, error.message);
205
throw error;
206
}
207
}
208
}
209
210
// Conditional file operations based on error types
211
async function ensureFileExists(path: PortablePath, defaultContent: string): Promise<void> {
212
try {
213
await xfs.statPromise(path);
214
// File exists, nothing to do
215
} catch (error) {
216
if (error.code === 'ENOENT') {
217
// File doesn't exist, create it
218
await xfs.writeFilePromise(path, defaultContent);
219
} else {
220
// Other error, re-throw
221
throw error;
222
}
223
}
224
}
225
```
226
227
## Statistics Utilities
228
229
Utilities for working with file statistics, creating default stat objects, and comparing file metadata.
230
231
### Statistics Classes
232
233
```typescript { .api }
234
import { statUtils, type Stats, type BigIntStats, type Dirent } from '@yarnpkg/fslib';
235
236
// Directory entry with filesystem type information
237
class DirEntry<T extends Path> implements Dirent<T> {
238
constructor(
239
public name: Filename,
240
public path: T,
241
private stats: Stats
242
) {}
243
244
isFile(): boolean { return this.stats.isFile(); }
245
isDirectory(): boolean { return this.stats.isDirectory(); }
246
isBlockDevice(): boolean { return this.stats.isBlockDevice(); }
247
isCharacterDevice(): boolean { return this.stats.isCharacterDevice(); }
248
isSymbolicLink(): boolean { return this.stats.isSymbolicLink(); }
249
isFIFO(): boolean { return this.stats.isFIFO(); }
250
isSocket(): boolean { return this.stats.isSocket(); }
251
}
252
253
// File statistics entry (extends Node.js Stats)
254
class StatEntry implements Stats {
255
dev: number;
256
ino: number;
257
mode: number;
258
nlink: number;
259
uid: number;
260
gid: number;
261
rdev: number;
262
size: number;
263
blksize: number;
264
blocks: number;
265
atimeMs: number;
266
mtimeMs: number;
267
ctimeMs: number;
268
birthtimeMs: number;
269
atime: Date;
270
mtime: Date;
271
ctime: Date;
272
birthtime: Date;
273
crc?: number; // Optional CRC checksum
274
275
// Standard Stats methods...
276
}
277
```
278
279
### Statistics Constants and Utilities
280
281
```typescript { .api }
282
import { statUtils, constants } from '@yarnpkg/fslib';
283
284
// Default file mode
285
const DEFAULT_MODE = statUtils.DEFAULT_MODE; // S_IFREG | 0o644
286
287
// Create default statistics objects
288
const defaultStats = statUtils.makeDefaultStats();
289
const emptyStats = statUtils.makeEmptyStats();
290
291
// Working with stats objects
292
const stats1 = statUtils.makeDefaultStats();
293
stats1.size = 1024;
294
stats1.mode = constants.S_IFREG | 0o755;
295
296
const stats2 = statUtils.makeDefaultStats();
297
stats2.size = 1024;
298
stats2.mode = constants.S_IFREG | 0o755;
299
300
// Compare statistics objects
301
const areEqual = statUtils.areStatsEqual(stats1, stats2);
302
console.log('Stats are equal:', areEqual);
303
304
// Clear statistics (mutates object)
305
statUtils.clearStats(stats1);
306
console.log('Cleared size:', stats1.size); // 0
307
```
308
309
### Statistics Conversion
310
311
```typescript { .api }
312
import { statUtils, type Stats, type BigIntStats } from '@yarnpkg/fslib';
313
314
// Convert regular Stats to BigIntStats
315
const regularStats = statUtils.makeDefaultStats();
316
regularStats.size = 1024;
317
regularStats.mtimeMs = Date.now();
318
319
const bigIntStats: BigIntStats = statUtils.convertToBigIntStats(regularStats);
320
console.log('BigInt size:', bigIntStats.size); // 1024n
321
console.log('BigInt mtime:', bigIntStats.mtimeMs); // BigInt version
322
```
323
324
### Creating Custom Statistics
325
326
```typescript { .api }
327
import { statUtils, constants, type Stats } from '@yarnpkg/fslib';
328
329
// Create file statistics
330
function createFileStats(size: number, mtime: Date): Stats {
331
const stats = statUtils.makeDefaultStats();
332
333
stats.size = size;
334
stats.mode = constants.S_IFREG | 0o644; // Regular file, rw-r--r--
335
stats.mtime = mtime;
336
stats.mtimeMs = mtime.getTime();
337
stats.atime = mtime;
338
stats.atimeMs = mtime.getTime();
339
stats.ctime = mtime;
340
stats.ctimeMs = mtime.getTime();
341
342
return stats;
343
}
344
345
// Create directory statistics
346
function createDirectoryStats(mtime: Date): Stats {
347
const stats = statUtils.makeDefaultStats();
348
349
stats.size = 0;
350
stats.mode = constants.S_IFDIR | 0o755; // Directory, rwxr-xr-x
351
stats.mtime = mtime;
352
stats.mtimeMs = mtime.getTime();
353
stats.atime = mtime;
354
stats.atimeMs = mtime.getTime();
355
stats.ctime = mtime;
356
stats.ctimeMs = mtime.getTime();
357
358
return stats;
359
}
360
361
// Usage
362
const fileStats = createFileStats(2048, new Date());
363
const dirStats = createDirectoryStats(new Date());
364
365
console.log('Is file:', fileStats.isFile()); // true
366
console.log('Is directory:', dirStats.isDirectory()); // true
367
```
368
369
## Integration Examples
370
371
### Comprehensive File Operations
372
373
```typescript { .api }
374
import {
375
constants, errors, statUtils, xfs, ppath,
376
type PortablePath, type Stats
377
} from '@yarnpkg/fslib';
378
379
// Enhanced file information utility
380
async function getFileInfo(path: PortablePath): Promise<{
381
path: PortablePath;
382
type: string;
383
size: number;
384
mode: string;
385
exists: boolean;
386
readable: boolean;
387
writable: boolean;
388
}> {
389
try {
390
const stats = await xfs.statPromise(path);
391
392
// Determine file type using constants
393
let type: string;
394
if ((stats.mode & constants.S_IFMT) === constants.S_IFDIR) {
395
type = 'directory';
396
} else if ((stats.mode & constants.S_IFMT) === constants.S_IFREG) {
397
type = 'file';
398
} else if ((stats.mode & constants.S_IFMT) === constants.S_IFLNK) {
399
type = 'symlink';
400
} else {
401
type = 'other';
402
}
403
404
// Format mode as octal string
405
const mode = (stats.mode & 0o777).toString(8).padStart(3, '0');
406
407
// Test access permissions
408
let readable = false;
409
let writable = false;
410
411
try {
412
await xfs.accessPromise(path, constants.R_OK);
413
readable = true;
414
} catch {}
415
416
try {
417
await xfs.accessPromise(path, constants.W_OK);
418
writable = true;
419
} catch {}
420
421
return {
422
path,
423
type,
424
size: stats.size,
425
mode,
426
exists: true,
427
readable,
428
writable
429
};
430
431
} catch (error) {
432
if (error.code === 'ENOENT') {
433
return {
434
path,
435
type: 'none',
436
size: 0,
437
mode: '000',
438
exists: false,
439
readable: false,
440
writable: false
441
};
442
}
443
throw error;
444
}
445
}
446
447
// Usage
448
const info = await getFileInfo('/path/to/file.txt' as PortablePath);
449
console.log(`${info.path}: ${info.type} (${info.mode}) ${info.size} bytes`);
450
```
451
452
### Safe Timestamp Operations
453
454
```typescript { .api }
455
import { constants, statUtils, type Stats } from '@yarnpkg/fslib';
456
457
// Use safe timestamp for reproducible builds
458
function createReproducibleStats(size: number): Stats {
459
const stats = statUtils.makeDefaultStats();
460
461
stats.size = size;
462
stats.mode = constants.S_IFREG | 0o644;
463
464
// Use safe timestamp for reproducibility
465
const safeTime = new Date(constants.SAFE_TIME * 1000);
466
stats.mtime = safeTime;
467
stats.mtimeMs = constants.SAFE_TIME * 1000;
468
stats.atime = safeTime;
469
stats.atimeMs = constants.SAFE_TIME * 1000;
470
stats.ctime = safeTime;
471
stats.ctimeMs = constants.SAFE_TIME * 1000;
472
473
return stats;
474
}
475
476
// Compare file timestamps safely
477
function compareFileTimestamps(stats1: Stats, stats2: Stats): number {
478
const time1 = Math.floor(stats1.mtimeMs / 1000);
479
const time2 = Math.floor(stats2.mtimeMs / 1000);
480
481
if (time1 < time2) return -1;
482
if (time1 > time2) return 1;
483
return 0;
484
}
485
```
486
487
### Error Context Enhancement
488
489
```typescript { .api }
490
import { errors, type PortablePath } from '@yarnpkg/fslib';
491
492
// Enhanced error factory with context
493
class FileSystemError extends Error {
494
constructor(
495
public code: string,
496
message: string,
497
public path?: PortablePath,
498
public operation?: string
499
) {
500
super(message);
501
this.name = 'FileSystemError';
502
}
503
}
504
505
// Create contextual errors
506
function createContextualError(
507
operation: string,
508
path: PortablePath,
509
originalError: Error
510
): FileSystemError {
511
const message = `${operation} failed for ${path}: ${originalError.message}`;
512
return new FileSystemError(originalError.code || 'UNKNOWN', message, path, operation);
513
}
514
515
// Usage in filesystem operations
516
async function safeOperation<T>(
517
operation: string,
518
path: PortablePath,
519
fn: () => Promise<T>
520
): Promise<T> {
521
try {
522
return await fn();
523
} catch (error) {
524
throw createContextualError(operation, path, error);
525
}
526
}
527
528
// Example usage
529
try {
530
await safeOperation('read', '/missing/file.txt' as PortablePath, async () => {
531
return await xfs.readFilePromise('/missing/file.txt' as PortablePath, 'utf8');
532
});
533
} catch (error) {
534
console.error(`Operation failed: ${error.operation} on ${error.path}`);
535
console.error(`Error: ${error.message}`);
536
}
537
```
538
539
## Related Documentation
540
541
- [Filesystem Abstractions](./filesystem-abstractions.md) - Base classes that use these constants and utilities
542
- [Filesystem Implementations](./filesystem-implementations.md) - Concrete implementations using error handling
543
- [Path Handling](./path-handling.md) - Path constants and utilities
544
- [Advanced Features](./advanced-features.md) - Advanced operations building on these foundations