Parent directory

fs.ts

1976 bytes
 1import { constants } from "node:fs";
 2import fs from "node:fs/promises";
 3import { dirname, join } from "node:path";
 4
 5export interface FileSystem {
 6  readFile(path: string): Promise<string>;
 7  writeFileAtomic(path: string, content: string): Promise<void>;
 8  access(path: string, mode?: number): Promise<boolean>;
 9  readdir(path: string): Promise<Array<{ name: string; isDirectory: boolean; isFile: boolean; isSymbolicLink: boolean }>>;
10  stat(path: string): Promise<{ isDirectory: boolean; isFile: boolean; mode: number }>;
11}
12
13export class NodeFileSystem implements FileSystem {
14  async readFile(path: string): Promise<string> {
15    return fs.readFile(path, "utf8");
16  }
17
18  async writeFileAtomic(path: string, content: string): Promise<void> {
19    const dir = dirname(path);
20    const tmp = join(dir, `.pi-skill-toggle-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}.tmp`);
21    let mode: number | undefined;
22    try {
23      mode = (await fs.stat(path)).mode;
24    } catch {
25      mode = undefined;
26    }
27
28    await fs.writeFile(tmp, content, "utf8");
29    if (mode !== undefined) {
30      await fs.chmod(tmp, mode);
31    }
32    await fs.rename(tmp, path);
33  }
34
35  async access(path: string, mode = constants.F_OK): Promise<boolean> {
36    try {
37      await fs.access(path, mode);
38      return true;
39    } catch {
40      return false;
41    }
42  }
43
44  async readdir(path: string): Promise<Array<{ name: string; isDirectory: boolean; isFile: boolean; isSymbolicLink: boolean }>> {
45    const entries = await fs.readdir(path, { withFileTypes: true });
46    return entries.map((entry) => ({
47      name: entry.name,
48      isDirectory: entry.isDirectory(),
49      isFile: entry.isFile(),
50      isSymbolicLink: entry.isSymbolicLink(),
51    }));
52  }
53
54  async stat(path: string): Promise<{ isDirectory: boolean; isFile: boolean; mode: number }> {
55    const stats = await fs.stat(path);
56    return { isDirectory: stats.isDirectory(), isFile: stats.isFile(), mode: stats.mode };
57  }
58}