superlib 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -0
- package/dist/basic/BaseError.d.ts +4 -0
- package/dist/basic/BaseError.d.ts.map +1 -0
- package/dist/basic/BaseError.js +6 -0
- package/dist/basic/Result.d.ts +29 -0
- package/dist/basic/Result.d.ts.map +1 -0
- package/dist/basic/Result.js +55 -0
- package/dist/basic/ResultAsync.d.ts +9 -0
- package/dist/basic/ResultAsync.d.ts.map +1 -0
- package/dist/basic/ResultAsync.js +41 -0
- package/dist/basic/assert.d.ts +8 -0
- package/dist/basic/assert.d.ts.map +1 -0
- package/dist/basic/assert.js +25 -0
- package/dist/basic/index.d.ts +5 -0
- package/dist/basic/index.d.ts.map +1 -0
- package/dist/basic/index.js +4 -0
- package/dist/decorators/Retry.d.ts +3 -0
- package/dist/decorators/Retry.d.ts.map +1 -0
- package/dist/decorators/Retry.js +9 -0
- package/dist/decorators/Timeout.d.ts +3 -0
- package/dist/decorators/Timeout.d.ts.map +1 -0
- package/dist/decorators/Timeout.js +9 -0
- package/dist/decorators/common.d.ts +2 -0
- package/dist/decorators/common.d.ts.map +1 -0
- package/dist/decorators/common.js +17 -0
- package/dist/decorators/index.d.ts +3 -0
- package/dist/decorators/index.d.ts.map +1 -0
- package/dist/decorators/index.js +2 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/platform/JsonHttpClient/JsonHttpClient.d.ts +18 -0
- package/dist/platform/JsonHttpClient/JsonHttpClient.d.ts.map +1 -0
- package/dist/platform/JsonHttpClient/JsonHttpClient.js +48 -0
- package/dist/platform/JsonHttpClient/index.d.ts +2 -0
- package/dist/platform/JsonHttpClient/index.d.ts.map +1 -0
- package/dist/platform/JsonHttpClient/index.js +1 -0
- package/dist/platform/ProcessContext/EnvReader.d.ts +12 -0
- package/dist/platform/ProcessContext/EnvReader.d.ts.map +1 -0
- package/dist/platform/ProcessContext/EnvReader.js +56 -0
- package/dist/platform/ProcessContext/ProcessContext.d.ts +40 -0
- package/dist/platform/ProcessContext/ProcessContext.d.ts.map +1 -0
- package/dist/platform/ProcessContext/ProcessContext.js +57 -0
- package/dist/platform/ProcessContext/index.d.ts +3 -0
- package/dist/platform/ProcessContext/index.d.ts.map +1 -0
- package/dist/platform/ProcessContext/index.js +2 -0
- package/dist/platform/filesystem/AbsolutePath.d.ts +19 -0
- package/dist/platform/filesystem/AbsolutePath.d.ts.map +1 -0
- package/dist/platform/filesystem/AbsolutePath.js +31 -0
- package/dist/platform/filesystem/FileSystem.d.ts +21 -0
- package/dist/platform/filesystem/FileSystem.d.ts.map +1 -0
- package/dist/platform/filesystem/FileSystem.js +128 -0
- package/dist/platform/filesystem/IFileSystem.d.ts +55 -0
- package/dist/platform/filesystem/IFileSystem.d.ts.map +1 -0
- package/dist/platform/filesystem/IFileSystem.js +1 -0
- package/dist/platform/filesystem/MemoryFileSystem.d.ts +41 -0
- package/dist/platform/filesystem/MemoryFileSystem.d.ts.map +1 -0
- package/dist/platform/filesystem/MemoryFileSystem.js +185 -0
- package/dist/platform/filesystem/glob/glob.d.ts +14 -0
- package/dist/platform/filesystem/glob/glob.d.ts.map +1 -0
- package/dist/platform/filesystem/glob/glob.js +57 -0
- package/dist/platform/filesystem/glob/parseGlob.d.ts +11 -0
- package/dist/platform/filesystem/glob/parseGlob.d.ts.map +1 -0
- package/dist/platform/filesystem/glob/parseGlob.js +61 -0
- package/dist/platform/filesystem/index.d.ts +5 -0
- package/dist/platform/filesystem/index.d.ts.map +1 -0
- package/dist/platform/filesystem/index.js +4 -0
- package/dist/platform/getPort/checkPort.d.ts +2 -0
- package/dist/platform/getPort/checkPort.d.ts.map +1 -0
- package/dist/platform/getPort/checkPort.js +49 -0
- package/dist/platform/getPort/getPort.d.ts +8 -0
- package/dist/platform/getPort/getPort.d.ts.map +1 -0
- package/dist/platform/getPort/getPort.js +13 -0
- package/dist/platform/getPort/index.d.ts +2 -0
- package/dist/platform/getPort/index.d.ts.map +1 -0
- package/dist/platform/getPort/index.js +1 -0
- package/dist/platform/safeFetch/index.d.ts +2 -0
- package/dist/platform/safeFetch/index.d.ts.map +1 -0
- package/dist/platform/safeFetch/index.js +1 -0
- package/dist/platform/safeFetch/makeSafeFetch.d.ts +23 -0
- package/dist/platform/safeFetch/makeSafeFetch.d.ts.map +1 -0
- package/dist/platform/safeFetch/makeSafeFetch.js +19 -0
- package/dist/random/FixedRandom.d.ts +12 -0
- package/dist/random/FixedRandom.d.ts.map +1 -0
- package/dist/random/FixedRandom.js +43 -0
- package/dist/random/Random.d.ts +18 -0
- package/dist/random/Random.d.ts.map +1 -0
- package/dist/random/Random.js +24 -0
- package/dist/random/RealRandom.d.ts +5 -0
- package/dist/random/RealRandom.d.ts.map +1 -0
- package/dist/random/RealRandom.js +6 -0
- package/dist/random/SeededRandom.d.ts +10 -0
- package/dist/random/SeededRandom.d.ts.map +1 -0
- package/dist/random/SeededRandom.js +15 -0
- package/dist/random/index.d.ts +5 -0
- package/dist/random/index.d.ts.map +1 -0
- package/dist/random/index.js +4 -0
- package/dist/schema/StandardSchema.d.ts +60 -0
- package/dist/schema/StandardSchema.d.ts.map +1 -0
- package/dist/schema/StandardSchema.js +1 -0
- package/dist/schema/validateSchema.d.ts +9 -0
- package/dist/schema/validateSchema.d.ts.map +1 -0
- package/dist/schema/validateSchema.js +9 -0
- package/dist/task/all.d.ts +14 -0
- package/dist/task/all.d.ts.map +1 -0
- package/dist/task/all.js +61 -0
- package/dist/task/index.d.ts +15 -0
- package/dist/task/index.d.ts.map +1 -0
- package/dist/task/index.js +11 -0
- package/dist/task/pipe.d.ts +8 -0
- package/dist/task/pipe.d.ts.map +1 -0
- package/dist/task/pipe.js +11 -0
- package/dist/task/retry.d.ts +27 -0
- package/dist/task/retry.d.ts.map +1 -0
- package/dist/task/retry.js +61 -0
- package/dist/task/timeout.d.ts +21 -0
- package/dist/task/timeout.d.ts.map +1 -0
- package/dist/task/timeout.js +38 -0
- package/dist/task/types.d.ts +3 -0
- package/dist/task/types.d.ts.map +1 -0
- package/dist/task/types.js +1 -0
- package/dist/time/Clock.d.ts +18 -0
- package/dist/time/Clock.d.ts.map +1 -0
- package/dist/time/Clock.js +26 -0
- package/dist/time/index.d.ts +4 -0
- package/dist/time/index.d.ts.map +1 -0
- package/dist/time/index.js +3 -0
- package/dist/time/sleep.d.ts +3 -0
- package/dist/time/sleep.d.ts.map +1 -0
- package/dist/time/sleep.js +5 -0
- package/dist/time/temporal.d.ts +5 -0
- package/dist/time/temporal.d.ts.map +1 -0
- package/dist/time/temporal.js +41 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +1 -0
- package/package.json +70 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
declare class AbsolutePathClazz {
|
|
2
|
+
readonly path: string;
|
|
3
|
+
constructor(path: string);
|
|
4
|
+
getName(): string;
|
|
5
|
+
getDirPath(): AbsolutePath;
|
|
6
|
+
join(...paths: Array<string>): AbsolutePath;
|
|
7
|
+
/**
|
|
8
|
+
* Note: this returns a string representing relative path
|
|
9
|
+
*/
|
|
10
|
+
relativeFrom(root: AbsolutePath): string;
|
|
11
|
+
}
|
|
12
|
+
declare function AbsolutePathClass(absolutePath: string): AbsolutePathClazz;
|
|
13
|
+
declare namespace AbsolutePathClass {
|
|
14
|
+
var prototype: AbsolutePathClazz;
|
|
15
|
+
}
|
|
16
|
+
export declare const AbsolutePath: typeof AbsolutePathClass & typeof AbsolutePathClazz;
|
|
17
|
+
export type AbsolutePath = InstanceType<typeof AbsolutePathClazz>;
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=AbsolutePath.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AbsolutePath.d.ts","sourceRoot":"","sources":["../../../src/platform/filesystem/AbsolutePath.ts"],"names":[],"mappings":"AAGA,cAAM,iBAAiB;IACrB,SAAgB,IAAI,EAAE,MAAM,CAAA;gBAEhB,IAAI,EAAE,MAAM;IAMxB,OAAO,IAAI,MAAM;IAIjB,UAAU,IAAI,YAAY;IAI1B,IAAI,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,YAAY;IAI3C;;OAEG;IACH,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM;CAGzC;AAED,iBAAS,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,iBAAiB,CAElE;kBAFQ,iBAAiB;;;AAM1B,eAAO,MAAM,YAAY,qDAAsD,CAAA;AAC/E,MAAM,MAAM,YAAY,GAAG,YAAY,CAAC,OAAO,iBAAiB,CAAC,CAAA"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import * as pathModule from "node:path";
|
|
3
|
+
class AbsolutePathClazz {
|
|
4
|
+
path;
|
|
5
|
+
constructor(path) {
|
|
6
|
+
assert(pathModule.isAbsolute(path), `Path is not absolute, was: ${path}`);
|
|
7
|
+
const normalizedPath = pathModule.resolve(path);
|
|
8
|
+
this.path = normalizedPath;
|
|
9
|
+
}
|
|
10
|
+
getName() {
|
|
11
|
+
return pathModule.basename(this.path);
|
|
12
|
+
}
|
|
13
|
+
getDirPath() {
|
|
14
|
+
return AbsolutePath(pathModule.dirname(this.path));
|
|
15
|
+
}
|
|
16
|
+
join(...paths) {
|
|
17
|
+
return AbsolutePath(pathModule.join(this.path, ...paths));
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Note: this returns a string representing relative path
|
|
21
|
+
*/
|
|
22
|
+
relativeFrom(root) {
|
|
23
|
+
return pathModule.relative(root.path, this.path);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function AbsolutePathClass(absolutePath) {
|
|
27
|
+
return new AbsolutePathClazz(absolutePath);
|
|
28
|
+
}
|
|
29
|
+
Object.setPrototypeOf(AbsolutePathClass, AbsolutePathClazz);
|
|
30
|
+
AbsolutePathClass.prototype = AbsolutePathClazz.prototype;
|
|
31
|
+
export const AbsolutePath = Object.assign(AbsolutePathClass, AbsolutePathClazz);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { DirAccessError, DirRemoveError, FileAccessError, FileSystemEntry, FileWriteError, IFileSystem } from "./IFileSystem";
|
|
2
|
+
import { Result } from "../../basic/Result";
|
|
3
|
+
import { AbsolutePath } from "./AbsolutePath";
|
|
4
|
+
export declare class FileSystem implements IFileSystem {
|
|
5
|
+
readFile(path: AbsolutePath): Promise<Result<string, FileAccessError>>;
|
|
6
|
+
writeFile(path: AbsolutePath, contents: string): Promise<Result<void, FileWriteError>>;
|
|
7
|
+
exists(path: AbsolutePath): Promise<boolean>;
|
|
8
|
+
get(path: AbsolutePath): Promise<FileSystemEntry | undefined>;
|
|
9
|
+
listDirectory(path: AbsolutePath): Promise<Result<FileSystemEntry[], DirAccessError>>;
|
|
10
|
+
createDirectory(path: AbsolutePath, options: {
|
|
11
|
+
recursive: boolean;
|
|
12
|
+
}): Promise<void>;
|
|
13
|
+
removeDirectory(path: AbsolutePath, options: {
|
|
14
|
+
recursive: boolean;
|
|
15
|
+
force: boolean;
|
|
16
|
+
}): Promise<Result<void, DirRemoveError>>;
|
|
17
|
+
createTempDir(prefix: string): Promise<AsyncDisposable & {
|
|
18
|
+
path: AbsolutePath;
|
|
19
|
+
}>;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=FileSystem.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileSystem.d.ts","sourceRoot":"","sources":["../../../src/platform/filesystem/FileSystem.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EACd,eAAe,EACf,eAAe,EACf,cAAc,EACd,WAAW,EACZ,MAAM,eAAe,CAAA;AAEtB,OAAO,EAAW,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAEpD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAE7C,qBAAa,UAAW,YAAW,WAAW;IACtC,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAkBtE,SAAS,CAAC,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;IAgBtF,MAAM,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC;IAS5C,GAAG,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;IAoB7D,aAAa,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,EAAE,cAAc,CAAC,CAAC;IAqBrF,eAAe,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAInF,eAAe,CACnB,IAAI,EAAE,YAAY,EAClB,OAAO,EAAE;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,GAC9C,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;IA4ClC,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG;QAAE,IAAI,EAAE,YAAY,CAAA;KAAE,CAAC;CAWvF"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { access, mkdir, mkdtemp, readdir, readFile, rm, rmdir, stat, writeFile, } from "node:fs/promises";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import * as pathModule from "node:path";
|
|
4
|
+
import { Err, Ok, Result } from "../../basic/Result.js";
|
|
5
|
+
import { ResultAsync } from "../../basic/ResultAsync.js";
|
|
6
|
+
import { AbsolutePath } from "./AbsolutePath.js";
|
|
7
|
+
export class FileSystem {
|
|
8
|
+
async readFile(path) {
|
|
9
|
+
return ResultAsync.try(() => readFile(path.path, "utf8"), (error) => {
|
|
10
|
+
if (error instanceof Error && "code" in error) {
|
|
11
|
+
switch (error.code) {
|
|
12
|
+
case "ENOENT":
|
|
13
|
+
return { type: "fs/file-not-found", path };
|
|
14
|
+
case "EISDIR":
|
|
15
|
+
return { type: "fs/file-is-a-dir", path };
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return { type: "fs/other", cause: error };
|
|
19
|
+
}).toPromise();
|
|
20
|
+
}
|
|
21
|
+
async writeFile(path, contents) {
|
|
22
|
+
return ResultAsync.try(() => writeFile(path.path, contents, "utf8"), (error) => {
|
|
23
|
+
if (error instanceof Error && "code" in error) {
|
|
24
|
+
switch (error.code) {
|
|
25
|
+
case "EISDIR":
|
|
26
|
+
return { type: "fs/file-is-a-dir", path };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return { type: "fs/other", cause: error };
|
|
30
|
+
}).toPromise();
|
|
31
|
+
}
|
|
32
|
+
async exists(path) {
|
|
33
|
+
try {
|
|
34
|
+
await access(path.path);
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async get(path) {
|
|
42
|
+
try {
|
|
43
|
+
const stats = await stat(path.path);
|
|
44
|
+
if (stats.isFile()) {
|
|
45
|
+
return { type: "file", path };
|
|
46
|
+
}
|
|
47
|
+
if (stats.isDirectory()) {
|
|
48
|
+
return { type: "dir", path };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
if (error instanceof Error && "code" in error) {
|
|
53
|
+
switch (error.code) {
|
|
54
|
+
case "ENOENT":
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async listDirectory(path) {
|
|
62
|
+
const entry = await this.get(path);
|
|
63
|
+
if (entry === undefined) {
|
|
64
|
+
return Err({ type: "fs/dir-not-found", path });
|
|
65
|
+
}
|
|
66
|
+
if (entry.type === "file") {
|
|
67
|
+
return Err({ type: "fs/not-a-dir", path });
|
|
68
|
+
}
|
|
69
|
+
const entries = await readdir(path.path, { withFileTypes: true });
|
|
70
|
+
return Ok(entries.map((dirent) => {
|
|
71
|
+
const entryPath = path.join(dirent.name);
|
|
72
|
+
if (dirent.isDirectory()) {
|
|
73
|
+
return { type: "dir", path: entryPath };
|
|
74
|
+
}
|
|
75
|
+
return { type: "file", path: entryPath };
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
async createDirectory(path, options) {
|
|
79
|
+
await mkdir(path.path, { recursive: options.recursive });
|
|
80
|
+
}
|
|
81
|
+
async removeDirectory(path, options) {
|
|
82
|
+
// @todo it might be possible to simplify this
|
|
83
|
+
if (options.recursive) {
|
|
84
|
+
return ResultAsync.try(() => rm(path.path, { recursive: true, force: options.force }), (error) => {
|
|
85
|
+
if (error instanceof Error && "code" in error) {
|
|
86
|
+
switch (error.code) {
|
|
87
|
+
case "ENOENT":
|
|
88
|
+
return { type: "fs/dir-not-found", path };
|
|
89
|
+
case "ENOTDIR":
|
|
90
|
+
return { type: "fs/not-a-dir", path };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
throw error;
|
|
94
|
+
}).toPromise();
|
|
95
|
+
}
|
|
96
|
+
return ResultAsync.try(() => rmdir(path.path), (error) => {
|
|
97
|
+
if (error instanceof Error && "code" in error) {
|
|
98
|
+
switch (error.code) {
|
|
99
|
+
case "EEXIST":
|
|
100
|
+
case "ENOTEMPTY":
|
|
101
|
+
return { type: "fs/dir-not-empty", path };
|
|
102
|
+
case "ENOENT": {
|
|
103
|
+
if (options.force) {
|
|
104
|
+
return Ok();
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
return { type: "fs/dir-not-found", path };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
case "ENOTDIR": {
|
|
111
|
+
return { type: "fs/not-a-dir", path };
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
throw error;
|
|
116
|
+
}).toPromise();
|
|
117
|
+
}
|
|
118
|
+
async createTempDir(prefix) {
|
|
119
|
+
const tempDirPath = await mkdtemp(pathModule.join(tmpdir(), prefix));
|
|
120
|
+
const tempPath = AbsolutePath(tempDirPath);
|
|
121
|
+
return {
|
|
122
|
+
path: tempPath,
|
|
123
|
+
async [Symbol.asyncDispose]() {
|
|
124
|
+
await rm(tempPath.path, { recursive: true, force: true });
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { Result } from "../../basic/Result";
|
|
2
|
+
import type { AbsolutePath } from "./AbsolutePath";
|
|
3
|
+
export interface IFileSystem {
|
|
4
|
+
readFile(path: AbsolutePath): Promise<Result<string, FileAccessError>>;
|
|
5
|
+
writeFile(path: AbsolutePath, contents: string): Promise<Result<void, FileWriteError>>;
|
|
6
|
+
createDirectory(path: AbsolutePath, options: {
|
|
7
|
+
recursive: boolean;
|
|
8
|
+
}): Promise<void>;
|
|
9
|
+
removeDirectory(path: AbsolutePath, options: {
|
|
10
|
+
recursive: boolean;
|
|
11
|
+
force: boolean;
|
|
12
|
+
}): Promise<Result<void, DirRemoveError>>;
|
|
13
|
+
listDirectory(path: AbsolutePath): Promise<Result<FileSystemEntry[], DirAccessError>>;
|
|
14
|
+
get(path: AbsolutePath): Promise<FileSystemEntry | undefined>;
|
|
15
|
+
exists(path: AbsolutePath): Promise<boolean>;
|
|
16
|
+
createTempDir(prefix: string): Promise<AsyncDisposable & {
|
|
17
|
+
path: AbsolutePath;
|
|
18
|
+
}>;
|
|
19
|
+
}
|
|
20
|
+
export type FileSystemEntry = {
|
|
21
|
+
type: "file";
|
|
22
|
+
path: AbsolutePath;
|
|
23
|
+
} | {
|
|
24
|
+
type: "dir";
|
|
25
|
+
path: AbsolutePath;
|
|
26
|
+
};
|
|
27
|
+
export type FileAccessError = {
|
|
28
|
+
type: "fs/file-not-found";
|
|
29
|
+
path: AbsolutePath;
|
|
30
|
+
} | {
|
|
31
|
+
type: "fs/file-is-a-dir";
|
|
32
|
+
path: AbsolutePath;
|
|
33
|
+
} | {
|
|
34
|
+
type: "fs/other";
|
|
35
|
+
cause: unknown;
|
|
36
|
+
};
|
|
37
|
+
export type FileWriteError = {
|
|
38
|
+
type: "fs/file-is-a-dir";
|
|
39
|
+
path: AbsolutePath;
|
|
40
|
+
} | {
|
|
41
|
+
type: "fs/other";
|
|
42
|
+
cause: unknown;
|
|
43
|
+
};
|
|
44
|
+
export type DirAccessError = {
|
|
45
|
+
type: "fs/dir-not-found";
|
|
46
|
+
path: AbsolutePath;
|
|
47
|
+
} | {
|
|
48
|
+
type: "fs/not-a-dir";
|
|
49
|
+
path: AbsolutePath;
|
|
50
|
+
};
|
|
51
|
+
export type DirRemoveError = DirAccessError | {
|
|
52
|
+
type: "fs/dir-not-empty";
|
|
53
|
+
path: AbsolutePath;
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=IFileSystem.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IFileSystem.d.ts","sourceRoot":"","sources":["../../../src/platform/filesystem/IFileSystem.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAGlD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,CAAA;IACtE,SAAS,CAAC,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAA;IACtF,eAAe,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACnF,eAAe,CACb,IAAI,EAAE,YAAY,EAClB,OAAO,EAAE;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,GAC9C,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAA;IACxC,aAAa,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,EAAE,cAAc,CAAC,CAAC,CAAA;IAErF,GAAG,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAAA;IAC7D,MAAM,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAE5C,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG;QAAE,IAAI,EAAE,YAAY,CAAA;KAAE,CAAC,CAAA;CACjF;AAED,MAAM,MAAM,eAAe,GACvB;IACE,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,YAAY,CAAA;CACnB,GACD;IACE,IAAI,EAAE,KAAK,CAAA;IACX,IAAI,EAAE,YAAY,CAAA;CACnB,CAAA;AAEL,MAAM,MAAM,eAAe,GACvB;IACE,IAAI,EAAE,mBAAmB,CAAA;IACzB,IAAI,EAAE,YAAY,CAAA;CACnB,GACD;IACE,IAAI,EAAE,kBAAkB,CAAA;IACxB,IAAI,EAAE,YAAY,CAAA;CACnB,GACD;IACE,IAAI,EAAE,UAAU,CAAA;IAChB,KAAK,EAAE,OAAO,CAAA;CACf,CAAA;AAEL,MAAM,MAAM,cAAc,GACtB;IACE,IAAI,EAAE,kBAAkB,CAAA;IACxB,IAAI,EAAE,YAAY,CAAA;CACnB,GACD;IACE,IAAI,EAAE,UAAU,CAAA;IAChB,KAAK,EAAE,OAAO,CAAA;CACf,CAAA;AAEL,MAAM,MAAM,cAAc,GACtB;IACE,IAAI,EAAE,kBAAkB,CAAA;IACxB,IAAI,EAAE,YAAY,CAAA;CACnB,GACD;IACE,IAAI,EAAE,cAAc,CAAA;IACpB,IAAI,EAAE,YAAY,CAAA;CACnB,CAAA;AAEL,MAAM,MAAM,cAAc,GAAG,cAAc,GAAG;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,IAAI,EAAE,YAAY,CAAA;CAAE,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { DirAccessError, DirRemoveError, FileAccessError, FileSystemEntry, FileWriteError, IFileSystem } from "./IFileSystem";
|
|
2
|
+
import { type Result } from "../../basic/Result";
|
|
3
|
+
import { AbsolutePath } from "./AbsolutePath";
|
|
4
|
+
type TempDirHandle = AsyncDisposable & {
|
|
5
|
+
path: AbsolutePath;
|
|
6
|
+
};
|
|
7
|
+
type FsGenesis = {
|
|
8
|
+
[path: string]: FsGenesis | string;
|
|
9
|
+
};
|
|
10
|
+
export declare class MemoryFileSystem implements IFileSystem {
|
|
11
|
+
private readonly files;
|
|
12
|
+
private readonly directories;
|
|
13
|
+
private readonly root;
|
|
14
|
+
private tempDirCounter;
|
|
15
|
+
constructor(genesis?: FsGenesis);
|
|
16
|
+
private seedFromGenesis;
|
|
17
|
+
readFile(path: AbsolutePath): Promise<Result<string, FileAccessError>>;
|
|
18
|
+
writeFile(path: AbsolutePath, contents: string): Promise<Result<void, FileWriteError>>;
|
|
19
|
+
writeFileSync(path: AbsolutePath, contents: string): Result<void, FileWriteError>;
|
|
20
|
+
exists(path: AbsolutePath): Promise<boolean>;
|
|
21
|
+
createDirectory(dirPath: AbsolutePath, options: {
|
|
22
|
+
recursive: boolean;
|
|
23
|
+
}): Promise<void>;
|
|
24
|
+
createDirectorySync(dirPath: AbsolutePath, options: {
|
|
25
|
+
recursive: boolean;
|
|
26
|
+
}): void;
|
|
27
|
+
removeDirectory(dirPath: AbsolutePath, options: {
|
|
28
|
+
recursive: boolean;
|
|
29
|
+
force: boolean;
|
|
30
|
+
}): Promise<Result<void, DirRemoveError>>;
|
|
31
|
+
createTempDir(prefix: string): Promise<TempDirHandle>;
|
|
32
|
+
listDirectory(path: AbsolutePath): Promise<Result<FileSystemEntry[], DirAccessError>>;
|
|
33
|
+
get(path: AbsolutePath): Promise<FileSystemEntry | undefined>;
|
|
34
|
+
private getFiles;
|
|
35
|
+
private getDirs;
|
|
36
|
+
private removeDirectoryRecursive;
|
|
37
|
+
private isWithinDir;
|
|
38
|
+
private hasEntriesWithin;
|
|
39
|
+
}
|
|
40
|
+
export {};
|
|
41
|
+
//# sourceMappingURL=MemoryFileSystem.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MemoryFileSystem.d.ts","sourceRoot":"","sources":["../../../src/platform/filesystem/MemoryFileSystem.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EACd,eAAe,EACf,eAAe,EACf,cAAc,EACd,WAAW,EACZ,MAAM,eAAe,CAAA;AAEtB,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAE7C,KAAK,aAAa,GAAG,eAAe,GAAG;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,CAAA;AAE7D,KAAK,SAAS,GAAG;IAAE,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAAA;CAAE,CAAA;AAEvD,qBAAa,gBAAiB,YAAW,WAAW;IAClD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA4B;IAClD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoB;IAChD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoB;IAEzC,OAAO,CAAC,cAAc,CAAI;gBAEd,OAAO,GAAE,SAAc;IAMnC,OAAO,CAAC,eAAe;IAajB,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAatE,SAAS,CAAC,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;IAI5F,aAAa,CAAC,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,cAAc,CAAC;IAU3E,MAAM,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC;IAI5C,eAAe,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAI5F,mBAAmB,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI;IA4B3E,eAAe,CACnB,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,GAC9C,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;IA0BlC,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAiBrD,aAAa,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,EAAE,cAAc,CAAC,CAAC;IAcrF,GAAG,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;IAgBnE,OAAO,CAAC,QAAQ;IAOhB,OAAO,CAAC,OAAO;IASf,OAAO,CAAC,wBAAwB;IAgBhC,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,gBAAgB;CAezB"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import * as pathModule from "node:path";
|
|
2
|
+
import { Err, Ok } from "../../basic/Result.js";
|
|
3
|
+
import { AbsolutePath } from "./AbsolutePath.js";
|
|
4
|
+
export class MemoryFileSystem {
|
|
5
|
+
files = new Map();
|
|
6
|
+
directories = new Set();
|
|
7
|
+
root = AbsolutePath("/");
|
|
8
|
+
tempDirCounter = 0; // used for numbering of temp dirs
|
|
9
|
+
constructor(genesis = {}) {
|
|
10
|
+
this.directories.add(this.root.path);
|
|
11
|
+
this.seedFromGenesis(AbsolutePath("/"), genesis);
|
|
12
|
+
}
|
|
13
|
+
seedFromGenesis(cwd, genesis) {
|
|
14
|
+
for (const [key, value] of Object.entries(genesis)) {
|
|
15
|
+
const path = cwd.join(key);
|
|
16
|
+
if (typeof value === "string") {
|
|
17
|
+
this.createDirectorySync(path.getDirPath(), { recursive: true });
|
|
18
|
+
this.writeFileSync(path, value);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
this.createDirectorySync(path, { recursive: true });
|
|
22
|
+
this.seedFromGenesis(path, value);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async readFile(path) {
|
|
27
|
+
if (this.directories.has(path.path)) {
|
|
28
|
+
return Err({ type: "fs/file-is-a-dir", path });
|
|
29
|
+
}
|
|
30
|
+
const contents = this.files.get(path.path);
|
|
31
|
+
if (contents === undefined) {
|
|
32
|
+
return Err({ type: "fs/file-not-found", path });
|
|
33
|
+
}
|
|
34
|
+
return Ok(contents);
|
|
35
|
+
}
|
|
36
|
+
async writeFile(path, contents) {
|
|
37
|
+
return this.writeFileSync(path, contents);
|
|
38
|
+
}
|
|
39
|
+
writeFileSync(path, contents) {
|
|
40
|
+
this.createDirectorySync(path.getDirPath(), { recursive: true });
|
|
41
|
+
if (this.directories.has(path.path)) {
|
|
42
|
+
return Err({ type: "fs/file-is-a-dir", path });
|
|
43
|
+
}
|
|
44
|
+
this.files.set(path.path, contents);
|
|
45
|
+
return Ok();
|
|
46
|
+
}
|
|
47
|
+
async exists(path) {
|
|
48
|
+
return (await this.get(path)) !== undefined;
|
|
49
|
+
}
|
|
50
|
+
async createDirectory(dirPath, options) {
|
|
51
|
+
this.createDirectorySync(dirPath, options);
|
|
52
|
+
}
|
|
53
|
+
createDirectorySync(dirPath, options) {
|
|
54
|
+
if (this.directories.has(dirPath.path)) {
|
|
55
|
+
if (!options.recursive) {
|
|
56
|
+
throw new Error(`Directory already exists: ${dirPath.path}`);
|
|
57
|
+
}
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const parentPath = dirPath.getDirPath();
|
|
61
|
+
if (!options.recursive && !this.directories.has(parentPath.path)) {
|
|
62
|
+
throw new Error(`Parent directory does not exist: ${parentPath.path}`);
|
|
63
|
+
}
|
|
64
|
+
if (options.recursive) {
|
|
65
|
+
let current = dirPath.path;
|
|
66
|
+
while (!this.directories.has(current)) {
|
|
67
|
+
this.directories.add(current);
|
|
68
|
+
const parentPath = pathModule.dirname(current);
|
|
69
|
+
if (parentPath === current) {
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
current = parentPath;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
this.directories.add(dirPath.path);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async removeDirectory(dirPath, options) {
|
|
80
|
+
if (this.files.has(dirPath.path)) {
|
|
81
|
+
return Err({ type: "fs/not-a-dir", path: dirPath });
|
|
82
|
+
}
|
|
83
|
+
if (!this.directories.has(dirPath.path)) {
|
|
84
|
+
if (options.force) {
|
|
85
|
+
return Ok();
|
|
86
|
+
}
|
|
87
|
+
return Err({ type: "fs/dir-not-found", path: dirPath });
|
|
88
|
+
}
|
|
89
|
+
const hasChildren = this.hasEntriesWithin(dirPath.path);
|
|
90
|
+
if (hasChildren && !options.recursive) {
|
|
91
|
+
return Err({ type: "fs/dir-not-empty", path: dirPath });
|
|
92
|
+
}
|
|
93
|
+
if (options.recursive) {
|
|
94
|
+
this.removeDirectoryRecursive(dirPath.path);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
this.directories.delete(dirPath.path);
|
|
98
|
+
}
|
|
99
|
+
return Ok();
|
|
100
|
+
}
|
|
101
|
+
async createTempDir(prefix) {
|
|
102
|
+
const tempDirPath = this.root.join("tmp", `${prefix}${this.tempDirCounter++}`);
|
|
103
|
+
await this.createDirectory(tempDirPath, { recursive: true });
|
|
104
|
+
const removeTempDir = () => {
|
|
105
|
+
this.removeDirectoryRecursive(tempDirPath.path);
|
|
106
|
+
};
|
|
107
|
+
return {
|
|
108
|
+
path: tempDirPath,
|
|
109
|
+
async [Symbol.asyncDispose]() {
|
|
110
|
+
removeTempDir();
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
async listDirectory(path) {
|
|
115
|
+
if (this.files.has(path.path)) {
|
|
116
|
+
return Err({ type: "fs/not-a-dir", path });
|
|
117
|
+
}
|
|
118
|
+
if (!this.directories.has(path.path)) {
|
|
119
|
+
return Err({ type: "fs/dir-not-found", path });
|
|
120
|
+
}
|
|
121
|
+
const files = this.getFiles(path.path).map((path) => ({ type: "file", path }));
|
|
122
|
+
const dirs = this.getDirs(path.path).map((path) => ({ type: "dir", path }));
|
|
123
|
+
return Ok([...files, ...dirs]);
|
|
124
|
+
}
|
|
125
|
+
async get(path) {
|
|
126
|
+
if (this.files.has(path.path)) {
|
|
127
|
+
return {
|
|
128
|
+
type: "file",
|
|
129
|
+
path,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
if (this.directories.has(path.path)) {
|
|
133
|
+
return {
|
|
134
|
+
type: "dir",
|
|
135
|
+
path,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
getFiles(dirPath) {
|
|
141
|
+
// Use Array.from() instead of iterator helpers for Node 20 compatibility
|
|
142
|
+
return Array.from(this.files.keys())
|
|
143
|
+
.filter((file) => pathModule.dirname(file) === dirPath)
|
|
144
|
+
.map((file) => AbsolutePath(file));
|
|
145
|
+
}
|
|
146
|
+
getDirs(dirPath) {
|
|
147
|
+
// Use Array.from() instead of iterator helpers for Node 20 compatibility
|
|
148
|
+
return Array.from(this.directories.keys())
|
|
149
|
+
.filter((dir) => pathModule.dirname(dir) === dirPath && dir !== dirPath)
|
|
150
|
+
.map((dir) => AbsolutePath(dir));
|
|
151
|
+
}
|
|
152
|
+
removeDirectoryRecursive(dirPath) {
|
|
153
|
+
for (const filePath of this.files.keys()) {
|
|
154
|
+
if (this.isWithinDir(filePath, dirPath)) {
|
|
155
|
+
this.files.delete(filePath);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
for (const directoryPath of Array.from(this.directories)) {
|
|
159
|
+
if (this.isWithinDir(directoryPath, dirPath)) {
|
|
160
|
+
this.directories.delete(directoryPath);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
this.directories.add(this.root.path);
|
|
164
|
+
}
|
|
165
|
+
isWithinDir(targetPath, dirPath) {
|
|
166
|
+
const relative = pathModule.relative(dirPath, targetPath);
|
|
167
|
+
if (relative === "") {
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
return !relative.startsWith(`../`) && relative !== ".." && !pathModule.isAbsolute(relative);
|
|
171
|
+
}
|
|
172
|
+
hasEntriesWithin(dirPath) {
|
|
173
|
+
for (const filePath of this.files.keys()) {
|
|
174
|
+
if (this.isWithinDir(filePath, dirPath) && filePath !== dirPath) {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
for (const directoryPath of this.directories) {
|
|
179
|
+
if (this.isWithinDir(directoryPath, dirPath) && directoryPath !== dirPath) {
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { FileSystemEntry, IFileSystem } from "../IFileSystem";
|
|
2
|
+
import { AbsolutePath } from "../AbsolutePath";
|
|
3
|
+
export interface GlobOptions {
|
|
4
|
+
pattern: string;
|
|
5
|
+
onlyFiles?: boolean;
|
|
6
|
+
cwd: AbsolutePath;
|
|
7
|
+
}
|
|
8
|
+
export declare function glob(options: GlobOptions & {
|
|
9
|
+
onlyFiles: true;
|
|
10
|
+
}, fs: IFileSystem): Promise<AbsolutePath[]>;
|
|
11
|
+
export declare function glob(options: GlobOptions & {
|
|
12
|
+
onlyFiles?: false;
|
|
13
|
+
}, fs: IFileSystem): Promise<FileSystemEntry[]>;
|
|
14
|
+
//# sourceMappingURL=glob.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glob.d.ts","sourceRoot":"","sources":["../../../../src/platform/filesystem/glob/glob.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAIlE,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAG9C,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,GAAG,EAAE,YAAY,CAAA;CAClB;AAED,wBAAsB,IAAI,CACxB,OAAO,EAAE,WAAW,GAAG;IAAE,SAAS,EAAE,IAAI,CAAA;CAAE,EAC1C,EAAE,EAAE,WAAW,GACd,OAAO,CAAC,YAAY,EAAE,CAAC,CAAA;AAC1B,wBAAsB,IAAI,CACxB,OAAO,EAAE,WAAW,GAAG;IAAE,SAAS,CAAC,EAAE,KAAK,CAAA;CAAE,EAC5C,EAAE,EAAE,WAAW,GACd,OAAO,CAAC,eAAe,EAAE,CAAC,CAAA"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { assert } from "../../../basic/index.js";
|
|
2
|
+
import { Task } from "../../../task/index.js";
|
|
3
|
+
import { AbsolutePath } from "../AbsolutePath.js";
|
|
4
|
+
import { parseGlob } from "./parseGlob.js";
|
|
5
|
+
export async function glob(options, fs) {
|
|
6
|
+
const cwd = options.cwd;
|
|
7
|
+
const chunks = parseGlob(options.pattern);
|
|
8
|
+
const entries = await matchGlobWalker(chunks, cwd, fs, new Set());
|
|
9
|
+
if (options.onlyFiles) {
|
|
10
|
+
return entries.filter((e) => e.type === "file").map((e) => e.path);
|
|
11
|
+
}
|
|
12
|
+
return entries;
|
|
13
|
+
}
|
|
14
|
+
async function matchGlobWalker(chunks, cwd, fs, visited) {
|
|
15
|
+
const cwdEntry = await fs.get(cwd);
|
|
16
|
+
assert(!!cwdEntry, `${cwd.path} does not exist`);
|
|
17
|
+
const [chunk, ...rest] = chunks;
|
|
18
|
+
if (!chunk) {
|
|
19
|
+
// @note: it's important that writing and checking 'visited' happens is not interrupted by async operation
|
|
20
|
+
const alreadyVisited = visited.has(cwd.path);
|
|
21
|
+
if (alreadyVisited) {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
visited.add(cwd.path);
|
|
25
|
+
return [cwdEntry];
|
|
26
|
+
}
|
|
27
|
+
if (cwdEntry.type === "file") {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
switch (chunk.type) {
|
|
31
|
+
case "literal": {
|
|
32
|
+
const newCwd = cwd.join(chunk.value);
|
|
33
|
+
if ((await fs.get(newCwd)) !== undefined) {
|
|
34
|
+
return matchGlobWalker(rest, newCwd, fs, visited);
|
|
35
|
+
}
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
case "pattern": {
|
|
39
|
+
const entities = (await fs.listDirectory(cwd)).unwrap();
|
|
40
|
+
const newCwds = entities
|
|
41
|
+
.filter((entity) => chunk.pattern.test(entity.path.getName()))
|
|
42
|
+
.map((m) => m.path);
|
|
43
|
+
const results = await Task.all(newCwds.map((newCwd) => () => matchGlobWalker(rest, newCwd, fs, visited)), { concurrency: "unbounded" });
|
|
44
|
+
return results.flat();
|
|
45
|
+
}
|
|
46
|
+
case "globstar": {
|
|
47
|
+
const entities = (await fs.listDirectory(cwd)).unwrap();
|
|
48
|
+
const newCwds = entities.map((m) => m.path);
|
|
49
|
+
const results = await Task.all([
|
|
50
|
+
() => matchGlobWalker(rest, cwd, fs, visited),
|
|
51
|
+
...newCwds.map((newCwd) => () => matchGlobWalker(rest, newCwd, fs, visited)), // without nesting
|
|
52
|
+
...newCwds.map((newCwd) => () => matchGlobWalker(chunks, newCwd, fs, visited)), // with nesting
|
|
53
|
+
], { concurrency: "unbounded" });
|
|
54
|
+
return results.flat();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parseGlob.d.ts","sourceRoot":"","sources":["../../../../src/platform/filesystem/glob/parseGlob.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,SAAS,GACjB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAAA;AAExB,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,CAItD"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { assert } from "../../../basic/index.js";
|
|
2
|
+
export function parseGlob(pattern) {
|
|
3
|
+
const chunks = pattern.split("/").map(parseChunk);
|
|
4
|
+
return combineLiteralChunks(chunks);
|
|
5
|
+
}
|
|
6
|
+
function parseChunk(chunk) {
|
|
7
|
+
if (chunk === "**") {
|
|
8
|
+
return { type: "globstar" };
|
|
9
|
+
}
|
|
10
|
+
if (specialCharacters.some((c) => chunk.includes(c))) {
|
|
11
|
+
return { type: "pattern", pattern: globPatternToRegex(chunk) };
|
|
12
|
+
}
|
|
13
|
+
return { type: "literal", value: chunk };
|
|
14
|
+
}
|
|
15
|
+
const specialCharacters = ["*", "?", "{", "["];
|
|
16
|
+
function globPatternToRegex(globPattern) {
|
|
17
|
+
let regexString = "";
|
|
18
|
+
for (let i = 0; i < globPattern.length; i++) {
|
|
19
|
+
const c = globPattern[i];
|
|
20
|
+
if (c === "*") {
|
|
21
|
+
regexString += ".*";
|
|
22
|
+
}
|
|
23
|
+
else if (c === ".") {
|
|
24
|
+
regexString += "\\.";
|
|
25
|
+
}
|
|
26
|
+
else if (c === "?") {
|
|
27
|
+
regexString += ".";
|
|
28
|
+
}
|
|
29
|
+
else if (c === "{") {
|
|
30
|
+
const closingBracketIndex = globPattern.indexOf("}", i + 1);
|
|
31
|
+
assert(closingBracketIndex !== -1, `Glob pattern ${globPattern} incorrect: unbalanced {}`);
|
|
32
|
+
const optionsString = globPattern.slice(i + 1, closingBracketIndex);
|
|
33
|
+
const options = optionsString.split(",").map((s) => escapeRegex(s.trim()));
|
|
34
|
+
regexString += `(${options.join("|")})`;
|
|
35
|
+
i = closingBracketIndex;
|
|
36
|
+
}
|
|
37
|
+
else if (c === "[") {
|
|
38
|
+
assert(false, "[] not supported yet");
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
regexString += c;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return new RegExp(`^${regexString}$`);
|
|
45
|
+
}
|
|
46
|
+
function escapeRegex(str) {
|
|
47
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
48
|
+
}
|
|
49
|
+
function combineLiteralChunks(chunks) {
|
|
50
|
+
return chunks.reduce((acc, chunk) => {
|
|
51
|
+
if (chunk.type === "literal" && acc.length > 0) {
|
|
52
|
+
const lastChunk = acc[acc.length - 1];
|
|
53
|
+
if (lastChunk.type === "literal") {
|
|
54
|
+
lastChunk.value += "/" + chunk.value;
|
|
55
|
+
return acc;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
acc.push(chunk);
|
|
59
|
+
return acc;
|
|
60
|
+
}, []);
|
|
61
|
+
}
|