/
fs.ts
135 lines (128 loc) · 3.97 KB
/
fs.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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import { join } from "https://deno.land/std@0.136.0/path/mod.ts";
import cache from "./cache.ts";
import { getContentType } from "./mime.ts";
import util from "./util.ts";
/* check whether or not the given path exists as a directory. */
export async function existsDir(path: string): Promise<boolean> {
try {
const stat = await Deno.lstat(path);
return stat.isDirectory;
} catch (err) {
if (err instanceof Deno.errors.NotFound) {
return false;
}
throw err;
}
}
/* check whether or not the given path exists as regular file. */
export async function existsFile(path: string): Promise<boolean> {
try {
const stat = await Deno.lstat(path);
return stat.isFile;
} catch (err) {
if (err instanceof Deno.errors.NotFound) {
return false;
}
throw err;
}
}
/* find file in the directory */
export async function findFile(filenames: string[]): Promise<string | undefined> {
const cwd = Deno.cwd();
for (const filename of filenames) {
const fullPath = join(cwd, filename);
if (await existsFile(fullPath)) {
return fullPath;
}
}
return void 0;
}
// get files in the directory
export async function getFiles(
dir: string,
filter?: (filename: string) => boolean,
__path: string[] = [],
): Promise<string[]> {
const list: string[] = [];
if (await existsDir(dir)) {
for await (const dirEntry of Deno.readDir(dir)) {
if (dirEntry.isDirectory) {
list.push(...await getFiles(join(dir, dirEntry.name), filter, [...__path, dirEntry.name]));
} else {
const filename = [".", ...__path, dirEntry.name].join("/");
if (!filter || filter(filename)) {
list.push(filename);
}
}
}
}
return list;
}
/* read source code from fs/cdn/cache */
export async function readCode(
specifier: string,
): Promise<[code: string, contentType: string]> {
if (util.isLikelyHttpURL(specifier)) {
const url = new URL(specifier);
if (url.hostname === "esm.sh" && !url.searchParams.has("target")) {
url.searchParams.set("target", "esnext");
}
const res = await cache(url.href);
if (res.status >= 400) {
throw new Error(`fetch ${url.href}: ${res.status} - ${res.statusText}`);
}
return [await res.text(), res.headers.get("Content-Type") || getContentType(url.pathname)];
}
specifier = util.splitBy(specifier, "?")[0];
return [await Deno.readTextFile(specifier), getContentType(specifier)];
}
/* watch the given directory and its subdirectories */
export const watchFs = async (dir: string, listener: (kind: "create" | "remove" | "modify", path: string) => void) => {
const timers = new Map();
const debounce = (id: string, callback: () => void, delay: number) => {
if (timers.has(id)) {
clearTimeout(timers.get(id)!);
}
timers.set(
id,
setTimeout(() => {
timers.delete(id);
callback();
}, delay),
);
};
const w = Deno.watchFs(dir, { recursive: true });
const ignoreReg = /[\/\\](\.git(hub)?|\.vscode|vendor|node_modules|dist|output|target)[\/\\]/;
const ignore = (path: string) => ignoreReg.test(path) || path.endsWith(".DS_Store");
const allFiles = new Set<string>(
(await getFiles(dir)).map((name) => join(dir, name)).filter((path) => !ignore(path)),
);
for await (const { kind, paths } of w) {
if (kind !== "create" && kind !== "remove" && kind !== "modify") {
continue;
}
for (const path of paths) {
if (ignore(path)) {
continue;
}
debounce(kind + path, async () => {
try {
await Deno.lstat(path);
if (!allFiles.has(path)) {
allFiles.add(path);
listener("create", path);
} else {
listener("modify", path);
}
} catch (error) {
if (error instanceof Deno.errors.NotFound) {
allFiles.delete(path);
listener("remove", path);
} else {
console.warn("watchFs:", error);
}
}
}, 100);
}
}
};