-
Notifications
You must be signed in to change notification settings - Fork 295
/
FileSystem.ts
111 lines (97 loc) · 2.82 KB
/
FileSystem.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import _ from "lodash";
/**
* A filesystem interface that is meant to be compatible with
* the 'fs' module from Node.js.
* Allows for the use of similar inteface implementation on
* browsers.
*/
export interface GenericFileSystem {
writeFile(path: string, content: string, options?: any): Promise<void>;
readFile(path: string, options?: any): Promise<string>;
access(path: string): Promise<void>;
mkdir(path: string, options?: any): Promise<void>;
}
export interface WalkableFileSystem {
readdir(path: string): Promise<string[]>;
stat(path: string): Promise<any>;
}
/**
* A filesystem implementation that stores files in memory.
*/
export class InMemoryFileSystem implements GenericFileSystem {
private files: Record<string, any> = {};
async writeFile(path: string, content: string, options?: any): Promise<void> {
this.files[path] = _.cloneDeep(content);
}
async readFile(path: string, options?: any): Promise<string> {
if (!(path in this.files)) {
throw new Error(`File ${path} does not exist`);
}
return _.cloneDeep(this.files[path]);
}
async access(path: string): Promise<void> {
if (!(path in this.files)) {
throw new Error(`File ${path} does not exist`);
}
}
async mkdir(path: string, options?: any): Promise<void> {
this.files[path] = _.get(this.files, path, null);
}
}
export type CompleteFileSystem = GenericFileSystem & WalkableFileSystem;
export function getNodeFS(): CompleteFileSystem {
const fs = require("fs/promises");
return fs;
}
let fs = null;
try {
fs = getNodeFS();
} catch (e) {
fs = new InMemoryFileSystem();
}
export const DEFAULT_FS: GenericFileSystem | CompleteFileSystem =
fs as GenericFileSystem;
// FS utility functions
/**
* Checks if a file exists.
* Analogous to the os.path.exists function from Python.
* @param fs The filesystem to use.
* @param path The path to the file to check.
* @returns A promise that resolves to true if the file exists, false otherwise.
*/
export async function exists(
fs: GenericFileSystem,
path: string,
): Promise<boolean> {
try {
await fs.access(path);
return true;
} catch {
return false;
}
}
/**
* Recursively traverses a directory and yields all the paths to the files in it.
* @param fs The filesystem to use.
* @param dirPath The path to the directory to traverse.
*/
export async function* walk(
fs: WalkableFileSystem,
dirPath: string,
): AsyncIterable<string> {
if (fs instanceof InMemoryFileSystem) {
throw new Error(
"The InMemoryFileSystem does not support directory traversal.",
);
}
const entries = await fs.readdir(dirPath);
for (const entry of entries) {
const fullPath = `${dirPath}/${entry}`;
const stats = await fs.stat(fullPath);
if (stats.isDirectory()) {
yield* walk(fs, fullPath);
} else {
yield fullPath;
}
}
}