or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

directory-operations.mddirectory-walking.mdfilesystem-metadata.mdindex.mdpath-objects.mdpath-resolution.md

path-objects.mddocs/

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

```