shfs 0.1.0 → 0.1.2

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.
@@ -7,6 +7,7 @@ interface FS {
7
7
  readLines(path: string): Stream<string>;
8
8
  writeFile(path: string, content: Uint8Array): Promise<void>;
9
9
  deleteFile(path: string): Promise<void>;
10
+ deleteDirectory(path: string, recursive?: boolean): Promise<void>;
10
11
  readdir(path: string): Stream<string>;
11
12
  mkdir(path: string, recursive?: boolean): Promise<void>;
12
13
  stat(path: string): Promise<{
@@ -18,4 +19,4 @@ interface FS {
18
19
  }
19
20
  //#endregion
20
21
  export { Stream as n, FS as t };
21
- //# sourceMappingURL=fs-DSmBon4m.d.mts.map
22
+ //# sourceMappingURL=fs-D1tzq9TG.d.mts.map
package/dist/fs.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { n as Stream, t as FS } from "./fs-DSmBon4m.mjs";
1
+ import { n as Stream, t as FS } from "./fs-D1tzq9TG.mjs";
2
2
 
3
3
  //#region src/fs/memory.d.ts
4
4
  declare class MemoryFS implements FS {
@@ -11,6 +11,7 @@ declare class MemoryFS implements FS {
11
11
  readLines(path: string): Stream<string>;
12
12
  writeFile(path: string, content: Uint8Array): Promise<void>;
13
13
  deleteFile(path: string): Promise<void>;
14
+ deleteDirectory(path: string, recursive?: boolean): Promise<void>;
14
15
  readdir(glob: string): Stream<string>;
15
16
  mkdir(path: string, recursive?: boolean): Promise<void>;
16
17
  stat(path: string): Promise<{
package/dist/fs.mjs CHANGED
@@ -1,5 +1,12 @@
1
+ import picomatch from "picomatch";
2
+
1
3
  //#region src/fs/memory.ts
2
- const TRAILING_SLASH_REGEX = /\/$/;
4
+ const TRAILING_SLASH_REGEX = /\/+$/;
5
+ const MULTIPLE_SLASH_REGEX = /\/+/g;
6
+ function normalizePath(path) {
7
+ if (path === "" || path === "/") return "/";
8
+ return path.replace(TRAILING_SLASH_REGEX, "").replace(MULTIPLE_SLASH_REGEX, "/");
9
+ }
3
10
  var MemoryFS = class {
4
11
  files = /* @__PURE__ */ new Map();
5
12
  directories = /* @__PURE__ */ new Set();
@@ -12,40 +19,69 @@ var MemoryFS = class {
12
19
  });
13
20
  }
14
21
  setFile(path, content) {
22
+ const normalizedPath = normalizePath(path);
15
23
  const encoded = typeof content === "string" ? new TextEncoder().encode(content) : content;
16
- this.files.set(path, encoded);
17
- this.fileMetadata.set(path, {
24
+ this.files.set(normalizedPath, encoded);
25
+ this.fileMetadata.set(normalizedPath, {
18
26
  mtime: /* @__PURE__ */ new Date(),
19
27
  isDirectory: false
20
28
  });
21
29
  }
22
30
  async readFile(path) {
23
- const content = this.files.get(path);
31
+ const normalizedPath = normalizePath(path);
32
+ const content = this.files.get(normalizedPath);
24
33
  if (!content) throw new Error(`File not found: ${path}`);
25
34
  return content;
26
35
  }
27
36
  async *readLines(path) {
28
- const content = this.files.get(path);
37
+ const normalizedPath = normalizePath(path);
38
+ const content = this.files.get(normalizedPath);
29
39
  if (!content) throw new Error(`File not found: ${path}`);
30
40
  yield* new TextDecoder().decode(content).split("\n").filter((_, i, arr) => !(i === arr.length - 1 && arr[i] === ""));
31
41
  }
32
42
  async writeFile(path, content) {
33
- this.files.set(path, content);
43
+ const normalizedPath = normalizePath(path);
44
+ this.files.set(normalizedPath, content);
45
+ this.fileMetadata.set(normalizedPath, {
46
+ mtime: /* @__PURE__ */ new Date(),
47
+ isDirectory: false
48
+ });
34
49
  }
35
50
  async deleteFile(path) {
36
- if (!this.files.has(path)) throw new Error(`File not found: ${path}`);
37
- this.files.delete(path);
51
+ const normalizedPath = normalizePath(path);
52
+ if (!this.files.has(normalizedPath)) throw new Error(`File not found: ${path}`);
53
+ this.files.delete(normalizedPath);
54
+ this.fileMetadata.delete(normalizedPath);
55
+ }
56
+ async deleteDirectory(path, recursive = false) {
57
+ const normalizedPath = normalizePath(path);
58
+ if (normalizedPath === "/") throw new Error("rm: cannot remove '/'");
59
+ if (!this.directories.has(normalizedPath)) throw new Error(`No such file or directory: ${path}`);
60
+ const childPrefix = `${normalizedPath}/`;
61
+ const hasChildDirectories = Array.from(this.directories).some((directory) => directory !== normalizedPath && directory.startsWith(childPrefix));
62
+ const hasChildFiles = Array.from(this.files.keys()).some((filePath) => filePath.startsWith(childPrefix));
63
+ if (!recursive && (hasChildDirectories || hasChildFiles)) throw new Error(`Directory not empty: ${path}`);
64
+ for (const filePath of Array.from(this.files.keys())) if (filePath.startsWith(childPrefix)) {
65
+ this.files.delete(filePath);
66
+ this.fileMetadata.delete(filePath);
67
+ }
68
+ const directoriesToDelete = Array.from(this.directories).filter((directory) => directory === normalizedPath || directory.startsWith(childPrefix)).sort((a, b) => b.length - a.length);
69
+ for (const directory of directoriesToDelete) {
70
+ if (directory === "/") continue;
71
+ this.directories.delete(directory);
72
+ this.fileMetadata.delete(directory);
73
+ }
38
74
  }
39
75
  async *readdir(glob) {
40
- const pattern = glob.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]").replace(/\./g, "\\.");
41
- const regex = /* @__PURE__ */ new RegExp(`^${pattern}$`);
42
- const paths = Array.from(this.files.keys()).filter((path) => regex.test(path)).sort();
76
+ const isMatch = picomatch(glob, { dot: true });
77
+ const paths = Array.from(this.files.keys()).filter((path) => isMatch(path)).sort();
43
78
  for (const path of paths) yield path;
44
79
  }
45
80
  async mkdir(path, recursive = false) {
46
- if (this.directories.has(path) || this.files.has(path)) throw new Error(`Directory already exists: ${path}`);
81
+ const normalizedPath = normalizePath(path);
82
+ if (this.directories.has(normalizedPath) || this.files.has(normalizedPath)) throw new Error(`Directory already exists: ${path}`);
47
83
  if (recursive) {
48
- const parts = path.split("/").filter(Boolean);
84
+ const parts = normalizedPath.split("/").filter(Boolean);
49
85
  let current = "";
50
86
  for (const part of parts) {
51
87
  current += `/${part}`;
@@ -58,17 +94,17 @@ var MemoryFS = class {
58
94
  }
59
95
  }
60
96
  } else {
61
- const parentPath = path.substring(0, path.lastIndexOf("/")) || "/";
97
+ const parentPath = normalizedPath.substring(0, normalizedPath.lastIndexOf("/")) || "/";
62
98
  if (parentPath !== "/" && !this.directories.has(parentPath) && !this.files.has(parentPath)) throw new Error(`No such file or directory: ${parentPath}`);
63
- this.directories.add(path);
64
- this.fileMetadata.set(path, {
99
+ this.directories.add(normalizedPath);
100
+ this.fileMetadata.set(normalizedPath, {
65
101
  mtime: /* @__PURE__ */ new Date(),
66
102
  isDirectory: true
67
103
  });
68
104
  }
69
105
  }
70
106
  async stat(path) {
71
- const normalizedPath = path.replace(TRAILING_SLASH_REGEX, "") || "/";
107
+ const normalizedPath = normalizePath(path);
72
108
  if (this.directories.has(normalizedPath)) return {
73
109
  isDirectory: true,
74
110
  size: 0,
@@ -86,7 +122,8 @@ var MemoryFS = class {
86
122
  throw new Error(`No such file or directory: ${path}`);
87
123
  }
88
124
  async exists(path) {
89
- return this.files.has(path) || this.directories.has(path);
125
+ const normalizedPath = normalizePath(path);
126
+ return this.files.has(normalizedPath) || this.directories.has(normalizedPath);
90
127
  }
91
128
  };
92
129
 
package/dist/fs.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"fs.mjs","names":[],"sources":["../src/fs/memory.ts"],"sourcesContent":["import type { Stream } from '../stream';\nimport type { FS } from './fs';\n\nexport type { FS } from './fs';\n\nconst TRAILING_SLASH_REGEX = /\\/$/;\n\nexport class MemoryFS implements FS {\n\tprivate readonly files = new Map<string, Uint8Array>();\n\tprivate readonly directories = new Set<string>();\n\tprivate readonly fileMetadata = new Map<\n\t\tstring,\n\t\t{ mtime: Date; isDirectory: boolean }\n\t>();\n\n\tconstructor() {\n\t\t// Initialize root directory\n\t\tthis.directories.add('/');\n\t\tthis.fileMetadata.set('/', { mtime: new Date(), isDirectory: true });\n\t}\n\n\tsetFile(path: string, content: string | Uint8Array): void {\n\t\tconst encoded =\n\t\t\ttypeof content === 'string'\n\t\t\t\t? new TextEncoder().encode(content)\n\t\t\t\t: content;\n\t\tthis.files.set(path, encoded);\n\t\tthis.fileMetadata.set(path, { mtime: new Date(), isDirectory: false });\n\t}\n\n\tasync readFile(path: string): Promise<Uint8Array> {\n\t\tconst content = this.files.get(path);\n\t\tif (!content) {\n\t\t\tthrow new Error(`File not found: ${path}`);\n\t\t}\n\t\treturn content;\n\t}\n\n\tasync *readLines(path: string): Stream<string> {\n\t\tconst content = this.files.get(path);\n\t\tif (!content) {\n\t\t\tthrow new Error(`File not found: ${path}`);\n\t\t}\n\t\tconst text = new TextDecoder().decode(content);\n\t\tconst lines = text\n\t\t\t.split('\\n')\n\t\t\t.filter((_, i, arr) => !(i === arr.length - 1 && arr[i] === ''));\n\t\tyield* lines;\n\t}\n\n\tasync writeFile(path: string, content: Uint8Array): Promise<void> {\n\t\tthis.files.set(path, content);\n\t}\n\n\tasync deleteFile(path: string): Promise<void> {\n\t\tif (!this.files.has(path)) {\n\t\t\tthrow new Error(`File not found: ${path}`);\n\t\t}\n\t\tthis.files.delete(path);\n\t}\n\n\tasync *readdir(glob: string): Stream<string> {\n\t\tconst pattern = glob\n\t\t\t.replace(/\\*\\*/g, '.*')\n\t\t\t.replace(/\\*/g, '[^/]*')\n\t\t\t.replace(/\\?/g, '[^/]')\n\t\t\t.replace(/\\./g, '\\\\.');\n\n\t\tconst regex = new RegExp(`^${pattern}$`);\n\n\t\tconst paths = Array.from(this.files.keys())\n\t\t\t.filter((path) => regex.test(path))\n\t\t\t.sort();\n\n\t\tfor (const path of paths) {\n\t\t\tyield path;\n\t\t}\n\t}\n\n\tasync mkdir(path: string, recursive = false): Promise<void> {\n\t\tif (this.directories.has(path) || this.files.has(path)) {\n\t\t\tthrow new Error(`Directory already exists: ${path}`);\n\t\t}\n\n\t\tif (recursive) {\n\t\t\t// Create all parent directories\n\t\t\tconst parts = path.split('/').filter(Boolean);\n\t\t\tlet current = '';\n\t\t\tfor (const part of parts) {\n\t\t\t\tcurrent += `/${part}`;\n\t\t\t\tif (\n\t\t\t\t\t!(this.directories.has(current) || this.files.has(current))\n\t\t\t\t) {\n\t\t\t\t\tthis.directories.add(current);\n\t\t\t\t\tthis.fileMetadata.set(current, {\n\t\t\t\t\t\tmtime: new Date(),\n\t\t\t\t\t\tisDirectory: true,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// Check if parent directory exists\n\t\t\tconst parentPath = path.substring(0, path.lastIndexOf('/')) || '/';\n\t\t\tif (\n\t\t\t\tparentPath !== '/' &&\n\t\t\t\t!this.directories.has(parentPath) &&\n\t\t\t\t!this.files.has(parentPath)\n\t\t\t) {\n\t\t\t\tthrow new Error(`No such file or directory: ${parentPath}`);\n\t\t\t}\n\t\t\tthis.directories.add(path);\n\t\t\tthis.fileMetadata.set(path, {\n\t\t\t\tmtime: new Date(),\n\t\t\t\tisDirectory: true,\n\t\t\t});\n\t\t}\n\t}\n\n\tasync stat(\n\t\tpath: string\n\t): Promise<{ isDirectory: boolean; size: number; mtime: Date }> {\n\t\t// Normalize path by removing trailing slash\n\t\tconst normalizedPath = path.replace(TRAILING_SLASH_REGEX, '') || '/';\n\n\t\tif (this.directories.has(normalizedPath)) {\n\t\t\treturn {\n\t\t\t\tisDirectory: true,\n\t\t\t\tsize: 0,\n\t\t\t\tmtime:\n\t\t\t\t\tthis.fileMetadata.get(normalizedPath)?.mtime || new Date(),\n\t\t\t};\n\t\t}\n\n\t\tif (this.files.has(normalizedPath)) {\n\t\t\tconst content = this.files.get(normalizedPath);\n\t\t\tif (content === undefined) {\n\t\t\t\tthrow new Error(`No such file or directory: ${path}`);\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tisDirectory: false,\n\t\t\t\tsize: content.byteLength,\n\t\t\t\tmtime:\n\t\t\t\t\tthis.fileMetadata.get(normalizedPath)?.mtime || new Date(),\n\t\t\t};\n\t\t}\n\n\t\tthrow new Error(`No such file or directory: ${path}`);\n\t}\n\n\tasync exists(path: string): Promise<boolean> {\n\t\treturn this.files.has(path) || this.directories.has(path);\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"fs.mjs","names":[],"sources":["../src/fs/memory.ts"],"sourcesContent":["import picomatch from 'picomatch';\nimport type { Stream } from '../stream';\nimport type { FS } from './fs';\n\nexport type { FS } from './fs';\n\nconst TRAILING_SLASH_REGEX = /\\/+$/;\nconst MULTIPLE_SLASH_REGEX = /\\/+/g;\n\nfunction normalizePath(path: string): string {\n\tif (path === '' || path === '/') {\n\t\treturn '/';\n\t}\n\treturn path\n\t\t.replace(TRAILING_SLASH_REGEX, '')\n\t\t.replace(MULTIPLE_SLASH_REGEX, '/');\n}\n\nexport class MemoryFS implements FS {\n\tprivate readonly files = new Map<string, Uint8Array>();\n\tprivate readonly directories = new Set<string>();\n\tprivate readonly fileMetadata = new Map<\n\t\tstring,\n\t\t{ mtime: Date; isDirectory: boolean }\n\t>();\n\n\tconstructor() {\n\t\t// Initialize root directory\n\t\tthis.directories.add('/');\n\t\tthis.fileMetadata.set('/', { mtime: new Date(), isDirectory: true });\n\t}\n\n\tsetFile(path: string, content: string | Uint8Array): void {\n\t\tconst normalizedPath = normalizePath(path);\n\t\tconst encoded =\n\t\t\ttypeof content === 'string'\n\t\t\t\t? new TextEncoder().encode(content)\n\t\t\t\t: content;\n\t\tthis.files.set(normalizedPath, encoded);\n\t\tthis.fileMetadata.set(normalizedPath, {\n\t\t\tmtime: new Date(),\n\t\t\tisDirectory: false,\n\t\t});\n\t}\n\n\tasync readFile(path: string): Promise<Uint8Array> {\n\t\tconst normalizedPath = normalizePath(path);\n\t\tconst content = this.files.get(normalizedPath);\n\t\tif (!content) {\n\t\t\tthrow new Error(`File not found: ${path}`);\n\t\t}\n\t\treturn content;\n\t}\n\n\tasync *readLines(path: string): Stream<string> {\n\t\tconst normalizedPath = normalizePath(path);\n\t\tconst content = this.files.get(normalizedPath);\n\t\tif (!content) {\n\t\t\tthrow new Error(`File not found: ${path}`);\n\t\t}\n\t\tconst text = new TextDecoder().decode(content);\n\t\tconst lines = text\n\t\t\t.split('\\n')\n\t\t\t.filter((_, i, arr) => !(i === arr.length - 1 && arr[i] === ''));\n\t\tyield* lines;\n\t}\n\n\tasync writeFile(path: string, content: Uint8Array): Promise<void> {\n\t\tconst normalizedPath = normalizePath(path);\n\t\tthis.files.set(normalizedPath, content);\n\t\tthis.fileMetadata.set(normalizedPath, {\n\t\t\tmtime: new Date(),\n\t\t\tisDirectory: false,\n\t\t});\n\t}\n\n\tasync deleteFile(path: string): Promise<void> {\n\t\tconst normalizedPath = normalizePath(path);\n\t\tif (!this.files.has(normalizedPath)) {\n\t\t\tthrow new Error(`File not found: ${path}`);\n\t\t}\n\t\tthis.files.delete(normalizedPath);\n\t\tthis.fileMetadata.delete(normalizedPath);\n\t}\n\n\tasync deleteDirectory(path: string, recursive = false): Promise<void> {\n\t\tconst normalizedPath = normalizePath(path);\n\t\tif (normalizedPath === '/') {\n\t\t\tthrow new Error(\"rm: cannot remove '/'\");\n\t\t}\n\t\tif (!this.directories.has(normalizedPath)) {\n\t\t\tthrow new Error(`No such file or directory: ${path}`);\n\t\t}\n\n\t\tconst childPrefix = `${normalizedPath}/`;\n\t\tconst hasChildDirectories = Array.from(this.directories).some(\n\t\t\t(directory) =>\n\t\t\t\tdirectory !== normalizedPath &&\n\t\t\t\tdirectory.startsWith(childPrefix)\n\t\t);\n\t\tconst hasChildFiles = Array.from(this.files.keys()).some((filePath) =>\n\t\t\tfilePath.startsWith(childPrefix)\n\t\t);\n\n\t\tif (!recursive && (hasChildDirectories || hasChildFiles)) {\n\t\t\tthrow new Error(`Directory not empty: ${path}`);\n\t\t}\n\n\t\tfor (const filePath of Array.from(this.files.keys())) {\n\t\t\tif (filePath.startsWith(childPrefix)) {\n\t\t\t\tthis.files.delete(filePath);\n\t\t\t\tthis.fileMetadata.delete(filePath);\n\t\t\t}\n\t\t}\n\n\t\tconst directoriesToDelete = Array.from(this.directories)\n\t\t\t.filter(\n\t\t\t\t(directory) =>\n\t\t\t\t\tdirectory === normalizedPath ||\n\t\t\t\t\tdirectory.startsWith(childPrefix)\n\t\t\t)\n\t\t\t.sort((a, b) => b.length - a.length);\n\t\tfor (const directory of directoriesToDelete) {\n\t\t\tif (directory === '/') {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthis.directories.delete(directory);\n\t\t\tthis.fileMetadata.delete(directory);\n\t\t}\n\t}\n\n\tasync *readdir(glob: string): Stream<string> {\n\t\tconst isMatch = picomatch(glob, { dot: true });\n\n\t\tconst paths = Array.from(this.files.keys())\n\t\t\t.filter((path) => isMatch(path))\n\t\t\t.sort();\n\n\t\tfor (const path of paths) {\n\t\t\tyield path;\n\t\t}\n\t}\n\n\tasync mkdir(path: string, recursive = false): Promise<void> {\n\t\tconst normalizedPath = normalizePath(path);\n\t\tif (\n\t\t\tthis.directories.has(normalizedPath) ||\n\t\t\tthis.files.has(normalizedPath)\n\t\t) {\n\t\t\tthrow new Error(`Directory already exists: ${path}`);\n\t\t}\n\n\t\tif (recursive) {\n\t\t\t// Create all parent directories\n\t\t\tconst parts = normalizedPath.split('/').filter(Boolean);\n\t\t\tlet current = '';\n\t\t\tfor (const part of parts) {\n\t\t\t\tcurrent += `/${part}`;\n\t\t\t\tif (\n\t\t\t\t\t!(this.directories.has(current) || this.files.has(current))\n\t\t\t\t) {\n\t\t\t\t\tthis.directories.add(current);\n\t\t\t\t\tthis.fileMetadata.set(current, {\n\t\t\t\t\t\tmtime: new Date(),\n\t\t\t\t\t\tisDirectory: true,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// Check if parent directory exists\n\t\t\tconst parentPath =\n\t\t\t\tnormalizedPath.substring(0, normalizedPath.lastIndexOf('/')) ||\n\t\t\t\t'/';\n\t\t\tif (\n\t\t\t\tparentPath !== '/' &&\n\t\t\t\t!this.directories.has(parentPath) &&\n\t\t\t\t!this.files.has(parentPath)\n\t\t\t) {\n\t\t\t\tthrow new Error(`No such file or directory: ${parentPath}`);\n\t\t\t}\n\t\t\tthis.directories.add(normalizedPath);\n\t\t\tthis.fileMetadata.set(normalizedPath, {\n\t\t\t\tmtime: new Date(),\n\t\t\t\tisDirectory: true,\n\t\t\t});\n\t\t}\n\t}\n\n\tasync stat(\n\t\tpath: string\n\t): Promise<{ isDirectory: boolean; size: number; mtime: Date }> {\n\t\t// Normalize path by removing trailing slash\n\t\tconst normalizedPath = normalizePath(path);\n\n\t\tif (this.directories.has(normalizedPath)) {\n\t\t\treturn {\n\t\t\t\tisDirectory: true,\n\t\t\t\tsize: 0,\n\t\t\t\tmtime:\n\t\t\t\t\tthis.fileMetadata.get(normalizedPath)?.mtime || new Date(),\n\t\t\t};\n\t\t}\n\n\t\tif (this.files.has(normalizedPath)) {\n\t\t\tconst content = this.files.get(normalizedPath);\n\t\t\tif (content === undefined) {\n\t\t\t\tthrow new Error(`No such file or directory: ${path}`);\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tisDirectory: false,\n\t\t\t\tsize: content.byteLength,\n\t\t\t\tmtime:\n\t\t\t\t\tthis.fileMetadata.get(normalizedPath)?.mtime || new Date(),\n\t\t\t};\n\t\t}\n\n\t\tthrow new Error(`No such file or directory: ${path}`);\n\t}\n\n\tasync exists(path: string): Promise<boolean> {\n\t\tconst normalizedPath = normalizePath(path);\n\t\treturn (\n\t\t\tthis.files.has(normalizedPath) ||\n\t\t\tthis.directories.has(normalizedPath)\n\t\t);\n\t}\n}\n"],"mappings":"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"}
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { t as FS } from "./fs-DSmBon4m.mjs";
1
+ import { t as FS } from "./fs-D1tzq9TG.mjs";
2
2
 
3
3
  //#region src/record.d.ts
4
4
  interface FileRecord {
@@ -22,23 +22,24 @@ interface JsonRecord {
22
22
  type Record = FileRecord | LineRecord | JsonRecord;
23
23
  //#endregion
24
24
  //#region src/shell/shell.d.ts
25
+ interface ShellOptions {
26
+ cwd?: string;
27
+ }
28
+ interface ShellCommand {
29
+ cwd(path: string): ShellCommand;
30
+ json(): Promise<unknown[]>;
31
+ lines(): Promise<string[]>;
32
+ raw(): Promise<Record[]>;
33
+ stdout(): Promise<void>;
34
+ text(): Promise<string>;
35
+ }
25
36
  declare class Shell {
26
37
  private readonly fs;
27
- constructor(fs: FS);
28
- $: (strings: TemplateStringsArray, ...exprs: unknown[]) => {
29
- json(): Promise<unknown[]>;
30
- lines(): Promise<string[]>;
31
- raw(): Promise<Record[]>;
32
- stdout(): Promise<void>;
33
- text(): Promise<string>;
34
- };
35
- exec(strings: TemplateStringsArray, ...exprs: unknown[]): {
36
- json(): Promise<unknown[]>;
37
- lines(): Promise<string[]>;
38
- raw(): Promise<Record[]>;
39
- stdout(): Promise<void>;
40
- text(): Promise<string>;
41
- };
38
+ private currentCwd;
39
+ constructor(fs: FS, options?: ShellOptions);
40
+ $: (strings: TemplateStringsArray, ...exprs: unknown[]) => ShellCommand;
41
+ exec(strings: TemplateStringsArray, ...exprs: unknown[]): ShellCommand;
42
+ cwd(newCwd: string): void;
42
43
  private _exec;
43
44
  }
44
45
  //#endregion