0
# Path Objects
1
2
Individual filesystem entry objects providing lazy-loaded metadata, type checking, and path manipulation methods. Path objects implement the Node.js Dirent interface with extensive caching and cross-platform support.
3
4
## Capabilities
5
6
### Path Object Creation and Properties
7
8
Path objects are created through PathScurry operations and represent individual filesystem entries.
9
10
```typescript { .api }
11
abstract class PathBase implements Dirent {
12
/** Base filename or directory name */
13
readonly name: string;
14
15
/** True if this Path is the current working directory */
16
isCWD: boolean;
17
18
/** Parent directory path (for Dirent compatibility) */
19
readonly parentPath: string;
20
21
/** Deprecated alias for parentPath */
22
readonly path: string;
23
24
/** Root Path entry for this filesystem */
25
readonly root: PathBase;
26
27
/** Collection of all known roots */
28
readonly roots: { [k: string]: PathBase };
29
30
/** Parent Path object (undefined for root) */
31
readonly parent?: PathBase;
32
33
/** Whether path comparisons are case-insensitive */
34
readonly nocase: boolean;
35
36
/** Path separator for generating paths */
37
abstract readonly sep: string;
38
39
/** Path separator(s) for parsing paths */
40
abstract readonly splitSep: string | RegExp;
41
}
42
```
43
44
**Usage Examples:**
45
46
```typescript
47
import { PathScurry } from "path-scurry";
48
49
const pw = new PathScurry();
50
const file = pw.cwd.resolve("package.json");
51
52
console.log(`Name: ${file.name}`); // "package.json"
53
console.log(`Parent: ${file.parentPath}`); // "/path/to/project"
54
console.log(`Is CWD: ${file.isCWD}`); // false
55
console.log(`Case sensitive: ${!file.nocase}`); // platform dependent
56
console.log(`Separator: ${file.sep}`); // "/" or "\\"
57
```
58
59
### Path Navigation and Resolution
60
61
Navigate the filesystem tree and resolve relative paths.
62
63
```typescript { .api }
64
/**
65
* Resolve a path relative to this Path object
66
* @param path - Path to resolve (optional)
67
* @returns Resolved Path object
68
*/
69
resolve(path?: string): PathBase;
70
71
/**
72
* Get relative path from PathScurry cwd to this path
73
* @returns Relative path string
74
*/
75
relative(): string;
76
77
/**
78
* Get relative path using POSIX separators
79
* @returns Relative path string with forward slashes
80
*/
81
relativePosix(): string;
82
83
/**
84
* Get fully resolved absolute path
85
* @returns Absolute path string
86
*/
87
fullpath(): string;
88
89
/**
90
* Get fully resolved absolute path with POSIX separators
91
* @returns Absolute path string with forward slashes
92
*/
93
fullpathPosix(): string;
94
95
/**
96
* Get depth of this path within the directory tree
97
* @returns Depth level (root = 0)
98
*/
99
depth(): number;
100
101
/**
102
* Get or create child path (internal method)
103
* @param pathPart - Child path component (no separators allowed)
104
* @param opts - Path creation options
105
* @returns Child Path object
106
* @internal
107
*/
108
child(pathPart: string, opts?: PathOpts): PathBase;
109
```
110
111
**Usage Examples:**
112
113
```typescript
114
const pw = new PathScurry("/home/user");
115
const srcDir = pw.cwd.resolve("project/src");
116
117
// Path resolution
118
const indexFile = srcDir.resolve("index.js");
119
console.log(indexFile.fullpath()); // "/home/user/project/src/index.js"
120
121
// Relative paths
122
console.log(indexFile.relative()); // "project/src/index.js"
123
console.log(indexFile.relativePosix()); // "project/src/index.js" (always forward slashes)
124
125
// Path depth
126
console.log(pw.cwd.depth()); // depends on /home/user depth from root
127
console.log(indexFile.depth()); // cwd depth + 3
128
129
// Cross-platform paths
130
console.log(indexFile.fullpath()); // native separators
131
console.log(indexFile.fullpathPosix()); // always forward slashes
132
```
133
134
### File Type Checking
135
136
Determine the type of filesystem entry with multiple checking methods.
137
138
```typescript { .api }
139
/**
140
* Get the type of this filesystem entry
141
* @returns Type string describing the entry
142
*/
143
getType(): Type;
144
145
/**
146
* Check if path matches specific type
147
* @param type - Type to check against
148
* @returns True if path is of specified type
149
*/
150
isType(type: Type): boolean;
151
152
/**
153
* Check if path is a regular file
154
*/
155
isFile(): boolean;
156
157
/**
158
* Check if path is a directory
159
*/
160
isDirectory(): boolean;
161
162
/**
163
* Check if path is a symbolic link
164
*/
165
isSymbolicLink(): boolean;
166
167
/**
168
* Check if path type is unknown or undetermined
169
*/
170
isUnknown(): boolean;
171
172
/**
173
* Check if path is a FIFO pipe
174
*/
175
isFIFO(): boolean;
176
177
/**
178
* Check if path is a character device
179
*/
180
isCharacterDevice(): boolean;
181
182
/**
183
* Check if path is a block device
184
*/
185
isBlockDevice(): boolean;
186
187
/**
188
* Check if path is a socket
189
*/
190
isSocket(): boolean;
191
192
type Type = 'Unknown' | 'FIFO' | 'CharacterDevice' | 'Directory' |
193
'BlockDevice' | 'File' | 'SymbolicLink' | 'Socket';
194
```
195
196
**Usage Examples:**
197
198
```typescript
199
const pw = new PathScurry();
200
201
for await (const entry of pw.iterate("./")) {
202
const type = entry.getType();
203
console.log(`${entry.name}: ${type}`);
204
205
// Specific type checks
206
if (entry.isFile()) {
207
console.log(` File size: ${entry.size || 'unknown'}`);
208
} else if (entry.isDirectory()) {
209
const children = await entry.readdir();
210
console.log(` Contains ${children.length} entries`);
211
} else if (entry.isSymbolicLink()) {
212
const target = await entry.readlink();
213
console.log(` Links to: ${target?.fullpath() || 'unresolvable'}`);
214
}
215
216
// Generic type checking
217
if (entry.isType('Directory')) {
218
console.log(` This is also a directory`);
219
}
220
}
221
```
222
223
### Name Matching and Comparison
224
225
Safe path name comparison handling unicode normalization and case sensitivity.
226
227
```typescript { .api }
228
/**
229
* Check if path name matches given string
230
* Handles case sensitivity and unicode normalization correctly
231
* @param name - Name to compare against
232
* @returns True if names match
233
*/
234
isNamed(name: string): boolean;
235
```
236
237
**Usage Examples:**
238
239
```typescript
240
const pw = new PathScurry();
241
const file = pw.cwd.resolve("café.txt");
242
243
// NEVER do this - unsafe due to unicode normalization
244
if (file.name === "café.txt") { /* WRONG */ }
245
246
// ALWAYS use isNamed for safety
247
if (file.isNamed("café.txt")) {
248
console.log("Names match safely");
249
}
250
251
// Case sensitivity handling
252
const winPw = new PathScurryWin32();
253
const winFile = winPw.cwd.resolve("MyFile.TXT");
254
255
console.log(winFile.isNamed("myfile.txt")); // true on case-insensitive systems
256
console.log(winFile.isNamed("MyFile.TXT")); // always true
257
```
258
259
### Filesystem Operations
260
261
Perform filesystem operations directly on Path objects.
262
263
```typescript { .api }
264
/**
265
* Read directory contents (async)
266
* @returns Promise resolving to array of child Path objects
267
*/
268
readdir(): Promise<PathBase[]>;
269
270
/**
271
* Read directory contents (sync)
272
* @returns Array of child Path objects
273
*/
274
readdirSync(): PathBase[];
275
276
/**
277
* Node-style callback interface for reading directory
278
* @param cb - Callback receiving (error, entries)
279
* @param allowZalgo - Allow immediate callback execution
280
*/
281
readdirCB(
282
cb: (er: NodeJS.ErrnoException | null, entries: PathBase[]) => any,
283
allowZalgo?: boolean
284
): void;
285
286
/**
287
* Get file statistics (async)
288
* @returns Promise resolving to this Path with populated metadata
289
*/
290
lstat(): Promise<PathBase | undefined>;
291
292
/**
293
* Get file statistics (sync)
294
* @returns This Path with populated metadata or undefined
295
*/
296
lstatSync(): PathBase | undefined;
297
298
/**
299
* Read symbolic link target (async)
300
* @returns Promise resolving to target Path object
301
*/
302
readlink(): Promise<PathBase | undefined>;
303
304
/**
305
* Read symbolic link target (sync)
306
* @returns Target Path object or undefined
307
*/
308
readlinkSync(): PathBase | undefined;
309
310
/**
311
* Resolve to canonical path (async)
312
* @returns Promise resolving to canonical Path object
313
*/
314
realpath(): Promise<PathBase | undefined>;
315
316
/**
317
* Resolve to canonical path (sync)
318
* @returns Canonical Path object or undefined
319
*/
320
realpathSync(): PathBase | undefined;
321
```
322
323
**Usage Examples:**
324
325
```typescript
326
const pw = new PathScurry();
327
const dir = pw.cwd.resolve("./src");
328
329
// Directory operations
330
if (dir.canReaddir()) {
331
const files = await dir.readdir();
332
console.log(`Directory contains ${files.length} entries`);
333
334
// Process each file
335
for (const file of files) {
336
if (file.isFile()) {
337
const stats = await file.lstat();
338
if (stats) {
339
console.log(`${file.name}: ${stats.size} bytes`);
340
}
341
}
342
}
343
}
344
345
// Symlink handling
346
const link = pw.cwd.resolve("./symlink-to-config");
347
if (link.canReadlink()) {
348
const target = await link.readlink();
349
if (target) {
350
console.log(`Symlink points to: ${target.fullpath()}`);
351
const realPath = await target.realpath();
352
console.log(`Real path: ${realPath?.fullpath()}`);
353
}
354
}
355
356
// Callback-style directory reading
357
dir.readdirCB((err, entries) => {
358
if (err) {
359
console.error("Failed to read directory:", err);
360
return;
361
}
362
363
console.log("Directory entries:", entries.map(e => e.name));
364
});
365
```
366
367
### Caching and Status Methods
368
369
Check cache status and path state without performing filesystem operations.
370
371
```typescript { .api }
372
/**
373
* Get cached lstat result without filesystem access
374
* @returns Cached Path object or undefined if not cached
375
*/
376
lstatCached(): PathBase | undefined;
377
378
/**
379
* Get cached readlink result without filesystem access
380
* @returns Cached target Path or undefined if not cached
381
*/
382
readlinkCached(): PathBase | undefined;
383
384
/**
385
* Get cached realpath result without filesystem access
386
* @returns Cached real Path or undefined if not cached
387
*/
388
realpathCached(): PathBase | undefined;
389
390
/**
391
* Get cached directory contents without filesystem access
392
* @returns Array of cached child entries (may be empty)
393
*/
394
readdirCached(): PathBase[];
395
396
/**
397
* Check if directory contents have been successfully loaded
398
* @returns True if readdir has been called and succeeded
399
*/
400
calledReaddir(): boolean;
401
402
/**
403
* Check if this path can likely be read as a directory
404
* @returns True if directory reading might succeed
405
*/
406
canReaddir(): boolean;
407
408
/**
409
* Check if readlink operation might succeed
410
* @returns True if path might be a readable symbolic link
411
*/
412
canReadlink(): boolean;
413
414
/**
415
* Check if path is known to not exist
416
* @returns True if previous operations determined non-existence
417
*/
418
isENOENT(): boolean;
419
```
420
421
**Usage Examples:**
422
423
```typescript
424
const pw = new PathScurry();
425
const file = pw.cwd.resolve("./might-not-exist.txt");
426
427
// Check cache status before expensive operations
428
const cachedStats = file.lstatCached();
429
if (cachedStats) {
430
console.log("Already have file stats");
431
console.log(`File type: ${cachedStats.getType()}`);
432
console.log(`File size: ${cachedStats.size}`);
433
} else {
434
console.log("Need to call lstat() to get file info");
435
const stats = await file.lstat();
436
if (stats) {
437
console.log("File exists and stats loaded");
438
} else {
439
console.log("File does not exist or cannot be accessed");
440
}
441
}
442
443
// Check directory cache
444
const dir = pw.cwd.resolve("./some-directory");
445
if (dir.calledReaddir()) {
446
const cached = dir.readdirCached();
447
console.log(`Directory has ${cached.length} cached entries`);
448
} else if (dir.canReaddir()) {
449
console.log("Directory can be read but hasn't been yet");
450
const entries = await dir.readdir();
451
console.log(`Directory loaded with ${entries.length} entries`);
452
} else {
453
console.log("Directory cannot be read");
454
}
455
456
// Check existence
457
if (file.isENOENT()) {
458
console.log("File is known to not exist");
459
} else {
460
console.log("File existence status unknown or file exists");
461
}
462
```
463
464
### Platform-Specific Path Classes
465
466
Use specific Path implementations for cross-platform development.
467
468
```typescript { .api }
469
/**
470
* Windows-specific Path implementation
471
*/
472
class PathWin32 extends PathBase {
473
sep: '\\';
474
splitSep: RegExp; // matches both '/' and '\\'
475
}
476
477
/**
478
* POSIX-specific Path implementation
479
*/
480
class PathPosix extends PathBase {
481
sep: '/';
482
splitSep: '/';
483
}
484
```
485
486
**Usage Examples:**
487
488
```typescript
489
import { PathWin32, PathPosix } from "path-scurry";
490
491
// Force Windows path behavior
492
const winScurry = new PathScurryWin32("/project");
493
const winPath = winScurry.cwd.resolve("src\\file.js");
494
console.log(winPath.fullpath()); // "C:\\project\\src\\file.js"
495
console.log(winPath.fullpathPosix()); // "//?/C:/project/src/file.js"
496
497
// Force POSIX path behavior
498
const posixScurry = new PathScurryPosix("/project");
499
const posixPath = posixScurry.cwd.resolve("src/file.js");
500
console.log(posixPath.fullpath()); // "/project/src/file.js"
501
console.log(posixPath.fullpathPosix()); // "/project/src/file.js"
502
```
503
504
### Walking and Traversal
505
506
Path objects support walking operations for subtree traversal.
507
508
```typescript { .api }
509
/**
510
* Check if this directory should be walked based on filters
511
* @param dirs - Set of already-visited directories (cycle prevention)
512
* @param walkFilter - Optional filter function
513
* @returns True if directory should be traversed
514
*/
515
shouldWalk(
516
dirs: Set<PathBase | undefined>,
517
walkFilter?: (e: PathBase) => boolean
518
): boolean;
519
```
520
521
**Usage Example:**
522
523
```typescript
524
const pw = new PathScurry();
525
const visitedDirs = new Set<PathBase>();
526
527
async function walkDirectory(dir: PathBase) {
528
if (!dir.shouldWalk(visitedDirs, (entry) => !entry.name.startsWith("."))) {
529
return;
530
}
531
532
visitedDirs.add(dir);
533
const entries = await dir.readdir();
534
535
for (const entry of entries) {
536
if (entry.isFile()) {
537
console.log(`File: ${entry.fullpath()}`);
538
} else if (entry.isDirectory()) {
539
await walkDirectory(entry);
540
}
541
}
542
}
543
544
const rootDir = pw.cwd.resolve("./src");
545
await walkDirectory(rootDir);
546
```