shell-dsl 0.0.30 → 0.0.32

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.
Files changed (45) hide show
  1. package/README.md +27 -1
  2. package/dist/cjs/package.json +1 -1
  3. package/dist/cjs/src/commands/mv/mv.cjs +7 -1
  4. package/dist/cjs/src/commands/mv/mv.cjs.map +3 -3
  5. package/dist/cjs/src/commands/tree/tree.cjs +64 -27
  6. package/dist/cjs/src/commands/tree/tree.cjs.map +3 -3
  7. package/dist/cjs/src/fs/memfs-adapter.cjs +32 -110
  8. package/dist/cjs/src/fs/memfs-adapter.cjs.map +3 -3
  9. package/dist/cjs/src/fs/real-fs.cjs +34 -112
  10. package/dist/cjs/src/fs/real-fs.cjs.map +3 -3
  11. package/dist/cjs/src/fs/special-files.cjs +98 -0
  12. package/dist/cjs/src/fs/special-files.cjs.map +10 -0
  13. package/dist/cjs/src/index.cjs +2 -1
  14. package/dist/cjs/src/index.cjs.map +3 -3
  15. package/dist/cjs/src/interpreter/interpreter.cjs +9 -8
  16. package/dist/cjs/src/interpreter/interpreter.cjs.map +3 -3
  17. package/dist/cjs/src/utils/glob.cjs +129 -0
  18. package/dist/cjs/src/utils/glob.cjs.map +10 -0
  19. package/dist/cjs/src/utils/index.cjs +3 -1
  20. package/dist/cjs/src/utils/index.cjs.map +3 -3
  21. package/dist/mjs/package.json +1 -1
  22. package/dist/mjs/src/commands/mv/mv.mjs +7 -1
  23. package/dist/mjs/src/commands/mv/mv.mjs.map +3 -3
  24. package/dist/mjs/src/commands/tree/tree.mjs +64 -27
  25. package/dist/mjs/src/commands/tree/tree.mjs.map +3 -3
  26. package/dist/mjs/src/fs/memfs-adapter.mjs +38 -110
  27. package/dist/mjs/src/fs/memfs-adapter.mjs.map +3 -3
  28. package/dist/mjs/src/fs/real-fs.mjs +40 -112
  29. package/dist/mjs/src/fs/real-fs.mjs.map +3 -3
  30. package/dist/mjs/src/fs/special-files.mjs +58 -0
  31. package/dist/mjs/src/fs/special-files.mjs.map +10 -0
  32. package/dist/mjs/src/index.mjs +3 -2
  33. package/dist/mjs/src/index.mjs.map +2 -2
  34. package/dist/mjs/src/interpreter/interpreter.mjs +9 -8
  35. package/dist/mjs/src/interpreter/interpreter.mjs.map +3 -3
  36. package/dist/mjs/src/utils/glob.mjs +89 -0
  37. package/dist/mjs/src/utils/glob.mjs.map +10 -0
  38. package/dist/mjs/src/utils/index.mjs +3 -1
  39. package/dist/mjs/src/utils/index.mjs.map +3 -3
  40. package/dist/types/src/fs/real-fs.d.ts +0 -5
  41. package/dist/types/src/fs/special-files.d.ts +8 -0
  42. package/dist/types/src/index.d.ts +2 -1
  43. package/dist/types/src/utils/glob.d.ts +6 -0
  44. package/dist/types/src/utils/index.d.ts +1 -0
  45. package/package.json +1 -1
package/README.md CHANGED
@@ -564,7 +564,7 @@ import { echo, cat, grep, wc, cp, mv, touch, tee, tree, find, sed, awk, cut } fr
564
564
  | `mv` | Move/rename files/directories (`-n` no-clobber) |
565
565
  | `touch` | Create empty files or update timestamps (`-c` no-create) |
566
566
  | `tee` | Duplicate stdin to stdout and files (`-a` append) |
567
- | `tree` | Display directory structure as tree (`-a` all, `-d` dirs only, `-L <n>` depth) |
567
+ | `tree` | Display directory structure as tree (`-a` all, `-d` dirs only, `-L <n>` depth, `-I <pattern>` ignore, `--prune` remove empty dirs) |
568
568
  | `find` | Search for files (`-name`, `-iname`, `-type f\|d`, `-maxdepth`, `-mindepth`) |
569
569
  | `sed` | Stream editor (`s///`, `d`, `p`, `-n`, `-e`) |
570
570
  | `awk` | Pattern scanning (`{print $1}`, `-F`, `NF`, `NR`) |
@@ -616,6 +616,32 @@ interface VirtualFS {
616
616
  }
617
617
  ```
618
618
 
619
+ ### `globVirtualFS` Helper
620
+
621
+ If you're implementing a custom `VirtualFS`, especially a composite or mounted filesystem, you can reuse `globVirtualFS()` instead of writing glob traversal yourself:
622
+
623
+ ```ts
624
+ import { globVirtualFS, type VirtualFS } from "shell-dsl";
625
+
626
+ class CompositeFileSystem implements VirtualFS {
627
+ // ... implement readFile/readdir/stat/etc.
628
+
629
+ async glob(pattern: string, opts?: { cwd?: string }): Promise<string[]> {
630
+ return globVirtualFS(this, pattern, opts);
631
+ }
632
+ }
633
+ ```
634
+
635
+ `globVirtualFS()` walks the visible virtual tree using only `readdir()`, `stat()`, and `resolve()`, so it works correctly for filesystems that mount different host directories under one virtual namespace.
636
+
637
+ It supports the same shell-style patterns used by the interpreter:
638
+
639
+ - `*.txt` for segment wildcards
640
+ - `**/*.ts` for recursive matches
641
+ - `file-?.md` for single-character matches
642
+ - `{a,b}.json` for brace expansion
643
+ - `[ab].txt` for character classes
644
+
619
645
  ## Real Filesystem Access
620
646
 
621
647
  For scenarios where you need to access the real filesystem with sandboxing, use `FileSystem` or `ReadOnlyFileSystem`:
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "shell-dsl",
3
- "version": "0.0.30",
3
+ "version": "0.0.32",
4
4
  "type": "commonjs"
5
5
  }
@@ -43,6 +43,7 @@ __export(exports_mv, {
43
43
  });
44
44
  module.exports = __toCommonJS(exports_mv);
45
45
  var import_flag_parser = require("../../utils/flag-parser.cjs");
46
+ var import_special_files = require("../../fs/special-files.cjs");
46
47
  var spec = {
47
48
  name: "mv",
48
49
  flags: [
@@ -88,6 +89,11 @@ var mv = async (ctx) => {
88
89
  try {
89
90
  await ctx.fs.stat(srcPath);
90
91
  const finalDest = destIsDir ? ctx.fs.resolve(destPath, ctx.fs.basename(srcPath)) : destPath;
92
+ if (import_special_files.isDevNullPath(srcPath) || import_special_files.isDevNullPath(finalDest)) {
93
+ await ctx.stderr.writeText(`mv: cannot move to or from special file '${import_special_files.DEV_NULL_PATH}'
94
+ `);
95
+ return 1;
96
+ }
91
97
  if (noClobber) {
92
98
  const exists = await ctx.fs.exists(finalDest);
93
99
  if (exists)
@@ -132,4 +138,4 @@ async function moveDirectory(ctx, src, dest, noClobber) {
132
138
  await ctx.fs.rm(src, { recursive: true });
133
139
  }
134
140
 
135
- //# debugId=54B51AB3A551E2C964756E2164756E21
141
+ //# debugId=C4CE62A58C3EA07364756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/commands/mv/mv.ts"],
4
4
  "sourcesContent": [
5
- "import type { Command } from \"../../types.cjs\";\nimport { createFlagParser, type FlagDefinition } from \"../../utils/flag-parser.cjs\";\n\ninterface MvFlags {\n noClobber: boolean;\n}\n\nconst spec = {\n name: \"mv\",\n flags: [\n { short: \"n\", long: \"no-clobber\" },\n { short: \"f\", long: \"force\" },\n ] as FlagDefinition[],\n usage: \"mv [-nf] source ... dest\",\n};\n\nconst defaults: MvFlags = { noClobber: false };\n\nconst handler = (flags: MvFlags, flag: FlagDefinition) => {\n if (flag.short === \"n\") flags.noClobber = true;\n // -f is default behavior, so we don't need to do anything\n};\n\nconst parser = createFlagParser(spec, defaults, handler);\n\nexport const mv: Command = async (ctx) => {\n const result = parser.parse(ctx.args);\n\n if (result.error) {\n await parser.writeError(result.error, ctx.stderr);\n return 1;\n }\n\n const { noClobber } = result.flags;\n const paths = result.args;\n\n if (paths.length < 2) {\n await ctx.stderr.writeText(\"mv: missing destination file operand\\n\");\n return 1;\n }\n\n const sources = paths.slice(0, -1);\n const dest = paths[paths.length - 1]!;\n const destPath = ctx.fs.resolve(ctx.cwd, dest);\n\n // Check if destination is a directory\n let destIsDir = false;\n try {\n const stat = await ctx.fs.stat(destPath);\n destIsDir = stat.isDirectory();\n } catch {\n // Destination doesn't exist\n }\n\n // If multiple sources, dest must be a directory\n if (sources.length > 1 && !destIsDir) {\n await ctx.stderr.writeText(`mv: target '${dest}' is not a directory\\n`);\n return 1;\n }\n\n for (const source of sources) {\n const srcPath = ctx.fs.resolve(ctx.cwd, source);\n\n try {\n // Check source exists\n await ctx.fs.stat(srcPath);\n\n // Determine final destination path\n const finalDest = destIsDir\n ? ctx.fs.resolve(destPath, ctx.fs.basename(srcPath))\n : destPath;\n\n // Check if dest exists and noClobber\n if (noClobber) {\n const exists = await ctx.fs.exists(finalDest);\n if (exists) continue; // Skip silently\n }\n\n // Move: copy then delete\n const srcStat = await ctx.fs.stat(srcPath);\n if (srcStat.isDirectory()) {\n await moveDirectory(ctx, srcPath, finalDest, noClobber);\n } else {\n const content = await ctx.fs.readFile(srcPath);\n await ctx.fs.writeFile(finalDest, content);\n await ctx.fs.rm(srcPath);\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n await ctx.stderr.writeText(`mv: cannot stat '${source}': ${message}\\n`);\n return 1;\n }\n }\n\n return 0;\n};\n\nasync function moveDirectory(\n ctx: Parameters<Command>[0],\n src: string,\n dest: string,\n noClobber: boolean\n): Promise<void> {\n // Create destination directory\n await ctx.fs.mkdir(dest, { recursive: true });\n\n // Read source directory contents\n const entries = await ctx.fs.readdir(src);\n\n for (const entry of entries) {\n const srcPath = ctx.fs.resolve(src, entry);\n const destPath = ctx.fs.resolve(dest, entry);\n\n const stat = await ctx.fs.stat(srcPath);\n\n if (stat.isDirectory()) {\n await moveDirectory(ctx, srcPath, destPath, noClobber);\n } else {\n if (noClobber) {\n const exists = await ctx.fs.exists(destPath);\n if (exists) continue;\n }\n const content = await ctx.fs.readFile(srcPath);\n await ctx.fs.writeFile(destPath, content);\n }\n }\n\n // Remove source directory after copying\n await ctx.fs.rm(src, { recursive: true });\n}\n"
5
+ "import type { Command } from \"../../types.cjs\";\nimport { createFlagParser, type FlagDefinition } from \"../../utils/flag-parser.cjs\";\nimport { DEV_NULL_PATH, isDevNullPath } from \"../../fs/special-files.cjs\";\n\ninterface MvFlags {\n noClobber: boolean;\n}\n\nconst spec = {\n name: \"mv\",\n flags: [\n { short: \"n\", long: \"no-clobber\" },\n { short: \"f\", long: \"force\" },\n ] as FlagDefinition[],\n usage: \"mv [-nf] source ... dest\",\n};\n\nconst defaults: MvFlags = { noClobber: false };\n\nconst handler = (flags: MvFlags, flag: FlagDefinition) => {\n if (flag.short === \"n\") flags.noClobber = true;\n // -f is default behavior, so we don't need to do anything\n};\n\nconst parser = createFlagParser(spec, defaults, handler);\n\nexport const mv: Command = async (ctx) => {\n const result = parser.parse(ctx.args);\n\n if (result.error) {\n await parser.writeError(result.error, ctx.stderr);\n return 1;\n }\n\n const { noClobber } = result.flags;\n const paths = result.args;\n\n if (paths.length < 2) {\n await ctx.stderr.writeText(\"mv: missing destination file operand\\n\");\n return 1;\n }\n\n const sources = paths.slice(0, -1);\n const dest = paths[paths.length - 1]!;\n const destPath = ctx.fs.resolve(ctx.cwd, dest);\n\n // Check if destination is a directory\n let destIsDir = false;\n try {\n const stat = await ctx.fs.stat(destPath);\n destIsDir = stat.isDirectory();\n } catch {\n // Destination doesn't exist\n }\n\n // If multiple sources, dest must be a directory\n if (sources.length > 1 && !destIsDir) {\n await ctx.stderr.writeText(`mv: target '${dest}' is not a directory\\n`);\n return 1;\n }\n\n for (const source of sources) {\n const srcPath = ctx.fs.resolve(ctx.cwd, source);\n\n try {\n // Check source exists\n await ctx.fs.stat(srcPath);\n\n // Determine final destination path\n const finalDest = destIsDir\n ? ctx.fs.resolve(destPath, ctx.fs.basename(srcPath))\n : destPath;\n\n if (isDevNullPath(srcPath) || isDevNullPath(finalDest)) {\n await ctx.stderr.writeText(`mv: cannot move to or from special file '${DEV_NULL_PATH}'\\n`);\n return 1;\n }\n\n // Check if dest exists and noClobber\n if (noClobber) {\n const exists = await ctx.fs.exists(finalDest);\n if (exists) continue; // Skip silently\n }\n\n // Move: copy then delete\n const srcStat = await ctx.fs.stat(srcPath);\n if (srcStat.isDirectory()) {\n await moveDirectory(ctx, srcPath, finalDest, noClobber);\n } else {\n const content = await ctx.fs.readFile(srcPath);\n await ctx.fs.writeFile(finalDest, content);\n await ctx.fs.rm(srcPath);\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n await ctx.stderr.writeText(`mv: cannot stat '${source}': ${message}\\n`);\n return 1;\n }\n }\n\n return 0;\n};\n\nasync function moveDirectory(\n ctx: Parameters<Command>[0],\n src: string,\n dest: string,\n noClobber: boolean\n): Promise<void> {\n // Create destination directory\n await ctx.fs.mkdir(dest, { recursive: true });\n\n // Read source directory contents\n const entries = await ctx.fs.readdir(src);\n\n for (const entry of entries) {\n const srcPath = ctx.fs.resolve(src, entry);\n const destPath = ctx.fs.resolve(dest, entry);\n\n const stat = await ctx.fs.stat(srcPath);\n\n if (stat.isDirectory()) {\n await moveDirectory(ctx, srcPath, destPath, noClobber);\n } else {\n if (noClobber) {\n const exists = await ctx.fs.exists(destPath);\n if (exists) continue;\n }\n const content = await ctx.fs.readFile(srcPath);\n await ctx.fs.writeFile(destPath, content);\n }\n }\n\n // Remove source directory after copying\n await ctx.fs.rm(src, { recursive: true });\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "54B51AB3A551E2C964756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "C4CE62A58C3EA07364756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -51,11 +51,19 @@ var spec = {
51
51
  { short: "d" },
52
52
  { short: "L", takesValue: true },
53
53
  { short: "I", takesValue: true },
54
- { long: "dirsfirst" }
54
+ { long: "dirsfirst" },
55
+ { long: "prune" }
55
56
  ],
56
- usage: "tree [-adI] [-L level] [-I pattern] [--dirsfirst] [directory ...]"
57
+ usage: "tree [-adI] [-L level] [-I pattern] [--dirsfirst] [--prune] [directory ...]"
58
+ };
59
+ var defaults = {
60
+ all: false,
61
+ directoriesOnly: false,
62
+ maxDepth: Infinity,
63
+ dirsfirst: true,
64
+ prune: false,
65
+ ignorePatterns: []
57
66
  };
58
- var defaults = { all: false, directoriesOnly: false, maxDepth: Infinity, dirsfirst: true, ignorePatterns: [] };
59
67
  var handlerResult = {};
60
68
  var handler = (flags, flag, value) => {
61
69
  if (flag.short === "a")
@@ -64,6 +72,8 @@ var handler = (flags, flag, value) => {
64
72
  flags.directoriesOnly = true;
65
73
  if (flag.long === "dirsfirst")
66
74
  flags.dirsfirst = true;
75
+ if (flag.long === "prune")
76
+ flags.prune = true;
67
77
  if (flag.short === "I" && value) {
68
78
  if (flags.ignorePatterns === defaults.ignorePatterns) {
69
79
  flags.ignorePatterns = [];
@@ -93,7 +103,7 @@ var tree = async (ctx) => {
93
103
  await ctx.stderr.writeText(handlerResult.error);
94
104
  return 1;
95
105
  }
96
- const { all: showAll, directoriesOnly, maxDepth, ignorePatterns } = result.flags;
106
+ const { all: showAll, directoriesOnly, maxDepth, prune, ignorePatterns } = result.flags;
97
107
  const targetPath = result.args[0] ?? ".";
98
108
  if (maxDepth < 1) {
99
109
  await ctx.stderr.writeText(`tree: Invalid level, must be greater than 0
@@ -118,11 +128,14 @@ var tree = async (ctx) => {
118
128
  }
119
129
  let dirCount = 0;
120
130
  let fileCount = 0;
131
+ const entriesCache = new Map;
132
+ const visibleContentCache = new Map;
121
133
  await ctx.stdout.writeText(targetPath + `
122
134
  `);
123
- async function printTree(path, prefix, depth) {
124
- if (depth > maxDepth)
125
- return;
135
+ async function getEntries(path) {
136
+ const cached = entriesCache.get(path);
137
+ if (cached)
138
+ return cached;
126
139
  let entries = await ctx.fs.readdir(path);
127
140
  if (!showAll) {
128
141
  entries = entries.filter((e) => !e.startsWith("."));
@@ -131,39 +144,63 @@ var tree = async (ctx) => {
131
144
  entries = entries.filter((e) => !ignorePatterns.some((p) => import_match_glob.matchGlob(p, e)));
132
145
  }
133
146
  entries.sort();
147
+ const resolvedEntries = [];
148
+ for (const name of entries) {
149
+ const entryPath = ctx.fs.resolve(path, name);
150
+ try {
151
+ const entryStat = await ctx.fs.stat(entryPath);
152
+ resolvedEntries.push({ name, path: entryPath, isDir: entryStat.isDirectory() });
153
+ } catch {}
154
+ }
155
+ entriesCache.set(path, resolvedEntries);
156
+ return resolvedEntries;
157
+ }
158
+ async function hasVisibleContent(path) {
159
+ const cached = visibleContentCache.get(path);
160
+ if (cached !== undefined)
161
+ return cached;
162
+ const entries = await getEntries(path);
163
+ for (const entry of entries) {
164
+ if (!entry.isDir) {
165
+ visibleContentCache.set(path, true);
166
+ return true;
167
+ }
168
+ if (await hasVisibleContent(entry.path)) {
169
+ visibleContentCache.set(path, true);
170
+ return true;
171
+ }
172
+ }
173
+ visibleContentCache.set(path, false);
174
+ return false;
175
+ }
176
+ async function printTree(path, prefix, depth) {
177
+ if (depth > maxDepth)
178
+ return;
179
+ const entries = await getEntries(path);
134
180
  const dirEntries = [];
135
181
  const fileEntries = [];
136
182
  for (const entry of entries) {
137
- const entryPath = ctx.fs.resolve(path, entry);
138
- try {
139
- const entryStat = await ctx.fs.stat(entryPath);
140
- if (entryStat.isDirectory()) {
141
- dirEntries.push(entry);
142
- } else {
143
- fileEntries.push(entry);
183
+ if (entry.isDir) {
184
+ if (prune && !await hasVisibleContent(entry.path)) {
185
+ continue;
144
186
  }
145
- } catch {}
187
+ dirEntries.push(entry);
188
+ } else {
189
+ fileEntries.push(entry);
190
+ }
146
191
  }
147
192
  const sortedEntries = directoriesOnly ? dirEntries : [...dirEntries, ...fileEntries];
148
193
  for (let i = 0;i < sortedEntries.length; i++) {
149
194
  const entry = sortedEntries[i];
150
195
  const isLast = i === sortedEntries.length - 1;
151
196
  const connector = isLast ? "└── " : "├── ";
152
- const entryPath = ctx.fs.resolve(path, entry);
153
- let isDir = false;
154
- try {
155
- const entryStat = await ctx.fs.stat(entryPath);
156
- isDir = entryStat.isDirectory();
157
- } catch {
158
- continue;
159
- }
160
- await ctx.stdout.writeText(prefix + connector + entry + `
197
+ await ctx.stdout.writeText(prefix + connector + entry.name + `
161
198
  `);
162
- if (isDir) {
199
+ if (entry.isDir) {
163
200
  dirCount++;
164
201
  if (depth < maxDepth) {
165
202
  const newPrefix = prefix + (isLast ? " " : "│ ");
166
- await printTree(entryPath, newPrefix, depth + 1);
203
+ await printTree(entry.path, newPrefix, depth + 1);
167
204
  }
168
205
  } else {
169
206
  fileCount++;
@@ -179,4 +216,4 @@ ${dirCount} ${dirWord}, ${fileCount} ${fileWord}
179
216
  return 0;
180
217
  };
181
218
 
182
- //# debugId=84C05E2FE694C09E64756E2164756E21
219
+ //# debugId=F4832ADA9F283A9964756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/commands/tree/tree.ts"],
4
4
  "sourcesContent": [
5
- "import type { Command } from \"../../types.cjs\";\nimport { createFlagParser, type FlagDefinition } from \"../../utils/flag-parser.cjs\";\nimport { matchGlob } from \"../../utils/match-glob.cjs\";\n\ninterface TreeFlags {\n all: boolean;\n directoriesOnly: boolean;\n maxDepth: number;\n dirsfirst: boolean;\n ignorePatterns: string[];\n}\n\nconst spec = {\n name: \"tree\",\n flags: [\n { short: \"a\", long: \"all\" },\n { short: \"d\" },\n { short: \"L\", takesValue: true },\n { short: \"I\", takesValue: true },\n { long: \"dirsfirst\" },\n ] as FlagDefinition[],\n usage: \"tree [-adI] [-L level] [-I pattern] [--dirsfirst] [directory ...]\",\n};\n\nconst defaults: TreeFlags = { all: false, directoriesOnly: false, maxDepth: Infinity, dirsfirst: true, ignorePatterns: [] };\n\ninterface HandlerResult {\n error?: string;\n}\n\nlet handlerResult: HandlerResult = {};\n\nconst handler = (flags: TreeFlags, flag: FlagDefinition, value?: string) => {\n if (flag.short === \"a\") flags.all = true;\n if (flag.short === \"d\") flags.directoriesOnly = true;\n if (flag.long === \"dirsfirst\") flags.dirsfirst = true;\n if (flag.short === \"I\" && value) {\n if (flags.ignorePatterns === defaults.ignorePatterns) {\n flags.ignorePatterns = [];\n }\n flags.ignorePatterns.push(...value.split(\"|\"));\n }\n if (flag.short === \"L\" && value) {\n const depth = parseInt(value, 10);\n if (isNaN(depth) || !/^\\d+$/.test(value)) {\n handlerResult.error = `tree: -L option requires a numeric argument\\nusage: ${spec.usage}\\n`;\n } else {\n flags.maxDepth = depth;\n }\n }\n};\n\nconst parser = createFlagParser(spec, defaults, handler);\n\nexport const tree: Command = async (ctx) => {\n // Reset handler result for each invocation\n handlerResult = {};\n\n const result = parser.parse(ctx.args);\n\n if (result.error) {\n await parser.writeError(result.error, ctx.stderr);\n return 1;\n }\n\n if (handlerResult.error) {\n await ctx.stderr.writeText(handlerResult.error);\n return 1;\n }\n\n const { all: showAll, directoriesOnly, maxDepth, ignorePatterns } = result.flags;\n const targetPath = result.args[0] ?? \".\";\n\n // Validate maxDepth\n if (maxDepth < 1) {\n await ctx.stderr.writeText(\"tree: Invalid level, must be greater than 0\\n\");\n return 1;\n }\n\n const resolvedPath = ctx.fs.resolve(ctx.cwd, targetPath);\n\n // Check if path exists\n let stat;\n try {\n stat = await ctx.fs.stat(resolvedPath);\n } catch {\n await ctx.stderr.writeText(`tree: ${targetPath}: No such file or directory\\n`);\n return 1;\n }\n\n // If it's a file, just print the filename\n if (stat.isFile()) {\n await ctx.stdout.writeText(targetPath + \"\\n\\n0 directories, 1 file\\n\");\n return 0;\n }\n\n let dirCount = 0;\n let fileCount = 0;\n\n // Print root\n await ctx.stdout.writeText(targetPath + \"\\n\");\n\n // Recursive function to build tree\n async function printTree(path: string, prefix: string, depth: number): Promise<void> {\n if (depth > maxDepth) return;\n\n let entries = await ctx.fs.readdir(path);\n\n // Filter hidden files unless -a\n if (!showAll) {\n entries = entries.filter((e) => !e.startsWith(\".\"));\n }\n\n // Filter by -I ignore patterns\n if (ignorePatterns.length > 0) {\n entries = entries.filter((e) => !ignorePatterns.some((p) => matchGlob(p, e)));\n }\n\n // Sort entries\n entries.sort();\n\n // Separate dirs and files, dirs first\n const dirEntries: string[] = [];\n const fileEntries: string[] = [];\n\n for (const entry of entries) {\n const entryPath = ctx.fs.resolve(path, entry);\n try {\n const entryStat = await ctx.fs.stat(entryPath);\n if (entryStat.isDirectory()) {\n dirEntries.push(entry);\n } else {\n fileEntries.push(entry);\n }\n } catch {\n // Skip entries we can't stat\n }\n }\n\n // Combine: directories first, then files (unless directoriesOnly)\n const sortedEntries = directoriesOnly\n ? dirEntries\n : [...dirEntries, ...fileEntries];\n\n for (let i = 0; i < sortedEntries.length; i++) {\n const entry = sortedEntries[i]!;\n const isLast = i === sortedEntries.length - 1;\n const connector = isLast ? \"└── \" : \"├── \";\n const entryPath = ctx.fs.resolve(path, entry);\n\n let isDir = false;\n try {\n const entryStat = await ctx.fs.stat(entryPath);\n isDir = entryStat.isDirectory();\n } catch {\n continue;\n }\n\n await ctx.stdout.writeText(prefix + connector + entry + \"\\n\");\n\n if (isDir) {\n dirCount++;\n if (depth < maxDepth) {\n const newPrefix = prefix + (isLast ? \" \" : \"│ \");\n await printTree(entryPath, newPrefix, depth + 1);\n }\n } else {\n fileCount++;\n }\n }\n }\n\n await printTree(resolvedPath, \"\", 1);\n\n // Print summary\n const dirWord = dirCount === 1 ? \"directory\" : \"directories\";\n const fileWord = fileCount === 1 ? \"file\" : \"files\";\n await ctx.stdout.writeText(`\\n${dirCount} ${dirWord}, ${fileCount} ${fileWord}\\n`);\n\n return 0;\n};\n"
5
+ "import type { Command } from \"../../types.cjs\";\nimport { createFlagParser, type FlagDefinition } from \"../../utils/flag-parser.cjs\";\nimport { matchGlob } from \"../../utils/match-glob.cjs\";\n\ninterface TreeFlags {\n all: boolean;\n directoriesOnly: boolean;\n maxDepth: number;\n dirsfirst: boolean;\n prune: boolean;\n ignorePatterns: string[];\n}\n\nconst spec = {\n name: \"tree\",\n flags: [\n { short: \"a\", long: \"all\" },\n { short: \"d\" },\n { short: \"L\", takesValue: true },\n { short: \"I\", takesValue: true },\n { long: \"dirsfirst\" },\n { long: \"prune\" },\n ] as FlagDefinition[],\n usage: \"tree [-adI] [-L level] [-I pattern] [--dirsfirst] [--prune] [directory ...]\",\n};\n\nconst defaults: TreeFlags = {\n all: false,\n directoriesOnly: false,\n maxDepth: Infinity,\n dirsfirst: true,\n prune: false,\n ignorePatterns: [],\n};\n\ninterface HandlerResult {\n error?: string;\n}\n\nlet handlerResult: HandlerResult = {};\n\nconst handler = (flags: TreeFlags, flag: FlagDefinition, value?: string) => {\n if (flag.short === \"a\") flags.all = true;\n if (flag.short === \"d\") flags.directoriesOnly = true;\n if (flag.long === \"dirsfirst\") flags.dirsfirst = true;\n if (flag.long === \"prune\") flags.prune = true;\n if (flag.short === \"I\" && value) {\n if (flags.ignorePatterns === defaults.ignorePatterns) {\n flags.ignorePatterns = [];\n }\n flags.ignorePatterns.push(...value.split(\"|\"));\n }\n if (flag.short === \"L\" && value) {\n const depth = parseInt(value, 10);\n if (isNaN(depth) || !/^\\d+$/.test(value)) {\n handlerResult.error = `tree: -L option requires a numeric argument\\nusage: ${spec.usage}\\n`;\n } else {\n flags.maxDepth = depth;\n }\n }\n};\n\nconst parser = createFlagParser(spec, defaults, handler);\n\nexport const tree: Command = async (ctx) => {\n // Reset handler result for each invocation\n handlerResult = {};\n\n const result = parser.parse(ctx.args);\n\n if (result.error) {\n await parser.writeError(result.error, ctx.stderr);\n return 1;\n }\n\n if (handlerResult.error) {\n await ctx.stderr.writeText(handlerResult.error);\n return 1;\n }\n\n const { all: showAll, directoriesOnly, maxDepth, prune, ignorePatterns } = result.flags;\n const targetPath = result.args[0] ?? \".\";\n\n // Validate maxDepth\n if (maxDepth < 1) {\n await ctx.stderr.writeText(\"tree: Invalid level, must be greater than 0\\n\");\n return 1;\n }\n\n const resolvedPath = ctx.fs.resolve(ctx.cwd, targetPath);\n\n // Check if path exists\n let stat;\n try {\n stat = await ctx.fs.stat(resolvedPath);\n } catch {\n await ctx.stderr.writeText(`tree: ${targetPath}: No such file or directory\\n`);\n return 1;\n }\n\n // If it's a file, just print the filename\n if (stat.isFile()) {\n await ctx.stdout.writeText(targetPath + \"\\n\\n0 directories, 1 file\\n\");\n return 0;\n }\n\n let dirCount = 0;\n let fileCount = 0;\n const entriesCache = new Map<string, { name: string; path: string; isDir: boolean }[]>();\n const visibleContentCache = new Map<string, boolean>();\n\n // Print root\n await ctx.stdout.writeText(targetPath + \"\\n\");\n\n async function getEntries(path: string): Promise<{ name: string; path: string; isDir: boolean }[]> {\n const cached = entriesCache.get(path);\n if (cached) return cached;\n\n let entries = await ctx.fs.readdir(path);\n\n // Filter hidden files unless -a\n if (!showAll) {\n entries = entries.filter((e) => !e.startsWith(\".\"));\n }\n\n // Filter by -I ignore patterns\n if (ignorePatterns.length > 0) {\n entries = entries.filter((e) => !ignorePatterns.some((p) => matchGlob(p, e)));\n }\n\n // Sort entries\n entries.sort();\n\n const resolvedEntries: { name: string; path: string; isDir: boolean }[] = [];\n\n for (const name of entries) {\n const entryPath = ctx.fs.resolve(path, name);\n try {\n const entryStat = await ctx.fs.stat(entryPath);\n resolvedEntries.push({ name, path: entryPath, isDir: entryStat.isDirectory() });\n } catch {\n // Skip entries we can't stat\n }\n }\n\n entriesCache.set(path, resolvedEntries);\n return resolvedEntries;\n }\n\n async function hasVisibleContent(path: string): Promise<boolean> {\n const cached = visibleContentCache.get(path);\n if (cached !== undefined) return cached;\n\n const entries = await getEntries(path);\n\n for (const entry of entries) {\n if (!entry.isDir) {\n visibleContentCache.set(path, true);\n return true;\n }\n\n if (await hasVisibleContent(entry.path)) {\n visibleContentCache.set(path, true);\n return true;\n }\n }\n\n visibleContentCache.set(path, false);\n return false;\n }\n\n // Recursive function to build tree\n async function printTree(path: string, prefix: string, depth: number): Promise<void> {\n if (depth > maxDepth) return;\n\n const entries = await getEntries(path);\n\n // Separate dirs and files, dirs first\n const dirEntries: { name: string; path: string; isDir: boolean }[] = [];\n const fileEntries: { name: string; path: string; isDir: boolean }[] = [];\n\n for (const entry of entries) {\n if (entry.isDir) {\n if (prune && !(await hasVisibleContent(entry.path))) {\n continue;\n }\n dirEntries.push(entry);\n } else {\n fileEntries.push(entry);\n }\n }\n\n // Combine: directories first, then files (unless directoriesOnly)\n const sortedEntries = directoriesOnly\n ? dirEntries\n : [...dirEntries, ...fileEntries];\n\n for (let i = 0; i < sortedEntries.length; i++) {\n const entry = sortedEntries[i]!;\n const isLast = i === sortedEntries.length - 1;\n const connector = isLast ? \"└── \" : \"├── \";\n\n await ctx.stdout.writeText(prefix + connector + entry.name + \"\\n\");\n\n if (entry.isDir) {\n dirCount++;\n if (depth < maxDepth) {\n const newPrefix = prefix + (isLast ? \" \" : \"│ \");\n await printTree(entry.path, newPrefix, depth + 1);\n }\n } else {\n fileCount++;\n }\n }\n }\n\n await printTree(resolvedPath, \"\", 1);\n\n // Print summary\n const dirWord = dirCount === 1 ? \"directory\" : \"directories\";\n const fileWord = fileCount === 1 ? \"file\" : \"files\";\n await ctx.stdout.writeText(`\\n${dirCount} ${dirWord}, ${fileCount} ${fileWord}\\n`);\n\n return 0;\n};\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "84C05E2FE694C09E64756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "F4832ADA9F283A9964756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -67,21 +67,30 @@ __export(exports_memfs_adapter, {
67
67
  });
68
68
  module.exports = __toCommonJS(exports_memfs_adapter);
69
69
  var pathModule = __toESM(require("path"));
70
+ var import_glob = require("../utils/glob.cjs");
71
+ var import_special_files = require("./special-files.cjs");
70
72
  function createVirtualFS(memfs) {
71
73
  const { promises: fs } = memfs;
72
74
  return {
73
75
  readFile: async (path, encoding) => {
74
- if (path === "/dev/null")
75
- return encoding ? "" : Buffer.alloc(0);
76
+ const specialContent = import_special_files.readSpecialFile(path, encoding);
77
+ if (specialContent !== undefined)
78
+ return specialContent;
76
79
  const content = await fs.readFile(path);
77
80
  const buf = Buffer.from(content);
78
81
  return encoding ? buf.toString(encoding) : buf;
79
82
  },
80
83
  async readdir(path) {
84
+ const specialError = import_special_files.getSpecialPathError(path, "readdir");
85
+ if (specialError)
86
+ throw specialError;
81
87
  const entries = await fs.readdir(path);
82
88
  return entries.map(String);
83
89
  },
84
90
  async stat(path) {
91
+ const specialStat = import_special_files.statSpecialFile(path);
92
+ if (specialStat)
93
+ return specialStat;
85
94
  const stats = await fs.stat(path);
86
95
  return {
87
96
  isFile: () => stats.isFile(),
@@ -91,6 +100,9 @@ function createVirtualFS(memfs) {
91
100
  };
92
101
  },
93
102
  async exists(path) {
103
+ const specialExists = import_special_files.existsSpecialFile(path);
104
+ if (specialExists !== undefined)
105
+ return specialExists;
94
106
  try {
95
107
  await fs.stat(path);
96
108
  return true;
@@ -99,15 +111,27 @@ function createVirtualFS(memfs) {
99
111
  }
100
112
  },
101
113
  async writeFile(path, data) {
114
+ if (import_special_files.discardsSpecialFileWrites(path)) {
115
+ return;
116
+ }
102
117
  await fs.writeFile(path, data);
103
118
  },
104
119
  async appendFile(path, data) {
120
+ if (import_special_files.discardsSpecialFileWrites(path)) {
121
+ return;
122
+ }
105
123
  await fs.appendFile(path, data);
106
124
  },
107
125
  async mkdir(path, opts) {
126
+ const specialError = import_special_files.getSpecialPathError(path, "mkdir");
127
+ if (specialError)
128
+ throw specialError;
108
129
  await fs.mkdir(path, opts);
109
130
  },
110
131
  async rm(path, opts) {
132
+ const specialError = import_special_files.getSpecialPathError(path, "rm");
133
+ if (specialError)
134
+ throw specialError;
111
135
  try {
112
136
  const stats = await fs.stat(path);
113
137
  if (stats.isDirectory()) {
@@ -131,115 +155,13 @@ function createVirtualFS(memfs) {
131
155
  },
132
156
  async glob(pattern, opts) {
133
157
  const cwd = opts?.cwd ?? "/";
134
- return expandGlob(memfs, pattern, cwd);
158
+ return import_glob.globVirtualFS({
159
+ readdir: (filePath) => this.readdir(filePath),
160
+ stat: (filePath) => this.stat(filePath),
161
+ resolve: (...paths) => this.resolve(...paths)
162
+ }, pattern, { cwd });
135
163
  }
136
164
  };
137
165
  }
138
- async function expandGlob(memfs, pattern, cwd) {
139
- const { promises: fs } = memfs;
140
- const patterns = expandBraces(pattern);
141
- const allMatches = [];
142
- for (const pat of patterns) {
143
- const matches = await matchPattern(fs, pat, cwd);
144
- allMatches.push(...matches);
145
- }
146
- return [...new Set(allMatches)].sort();
147
- }
148
- function expandBraces(pattern) {
149
- const braceMatch = pattern.match(/\{([^{}]+)\}/);
150
- if (!braceMatch)
151
- return [pattern];
152
- const before = pattern.slice(0, braceMatch.index);
153
- const after = pattern.slice(braceMatch.index + braceMatch[0].length);
154
- const options = braceMatch[1].split(",");
155
- const results = [];
156
- for (const opt of options) {
157
- const expanded = expandBraces(before + opt + after);
158
- results.push(...expanded);
159
- }
160
- return results;
161
- }
162
- async function matchPattern(fs, pattern, cwd) {
163
- const parts = pattern.split("/").filter((p) => p !== "");
164
- const isAbsolute = pattern.startsWith("/");
165
- const startDir = isAbsolute ? "/" : cwd;
166
- return matchParts(fs, parts, startDir, isAbsolute);
167
- }
168
- async function matchParts(fs, parts, currentPath, isAbsolute) {
169
- if (parts.length === 0) {
170
- return [currentPath];
171
- }
172
- const [part, ...rest] = parts;
173
- if (part === "**") {
174
- const results = [];
175
- const withoutStar = await matchParts(fs, rest, currentPath, isAbsolute);
176
- results.push(...withoutStar);
177
- try {
178
- const entries = await fs.readdir(currentPath);
179
- for (const entry of entries) {
180
- const entryPath = pathModule.join(currentPath, String(entry));
181
- try {
182
- const stat = await fs.stat(entryPath);
183
- if (stat.isDirectory()) {
184
- const subMatches = await matchParts(fs, parts, entryPath, isAbsolute);
185
- results.push(...subMatches);
186
- }
187
- } catch {}
188
- }
189
- } catch {}
190
- return results;
191
- }
192
- const regex = globToRegex(part);
193
- try {
194
- const entries = await fs.readdir(currentPath);
195
- const results = [];
196
- for (const entry of entries) {
197
- const entryName = String(entry);
198
- if (regex.test(entryName)) {
199
- const entryPath = pathModule.join(currentPath, entryName);
200
- if (rest.length === 0) {
201
- results.push(entryPath);
202
- } else {
203
- try {
204
- const stat = await fs.stat(entryPath);
205
- if (stat.isDirectory()) {
206
- const subMatches = await matchParts(fs, rest, entryPath, isAbsolute);
207
- results.push(...subMatches);
208
- }
209
- } catch {}
210
- }
211
- }
212
- }
213
- return results;
214
- } catch {
215
- return [];
216
- }
217
- }
218
- function globToRegex(pattern) {
219
- let regex = "^";
220
- for (let i = 0;i < pattern.length; i++) {
221
- const char = pattern[i];
222
- if (char === "*") {
223
- regex += "[^/]*";
224
- } else if (char === "?") {
225
- regex += "[^/]";
226
- } else if (char === "[") {
227
- let j = i + 1;
228
- let classContent = "";
229
- while (j < pattern.length && pattern[j] !== "]") {
230
- classContent += pattern[j];
231
- j++;
232
- }
233
- regex += `[${classContent}]`;
234
- i = j;
235
- } else if (".+^${}()|\\".includes(char)) {
236
- regex += "\\" + char;
237
- } else {
238
- regex += char;
239
- }
240
- }
241
- regex += "$";
242
- return new RegExp(regex);
243
- }
244
166
 
245
- //# debugId=8EA2A9013D6F90A464756E2164756E21
167
+ //# debugId=F25975C12E3475B564756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/fs/memfs-adapter.ts"],
4
4
  "sourcesContent": [
5
- "import type { IFs } from \"memfs\";\nimport type { VirtualFS, FileStat } from \"../types.cjs\";\nimport * as pathModule from \"path\";\n\nexport function createVirtualFS(memfs: IFs): VirtualFS {\n const { promises: fs } = memfs;\n\n return {\n readFile: (async (path: string, encoding?: BufferEncoding): Promise<Buffer | string> => {\n if (path === \"/dev/null\") return encoding ? \"\" : Buffer.alloc(0);\n const content = await fs.readFile(path);\n const buf = Buffer.from(content);\n return encoding ? buf.toString(encoding) : buf;\n }) as VirtualFS[\"readFile\"],\n\n async readdir(path: string): Promise<string[]> {\n const entries = await fs.readdir(path);\n return entries.map(String);\n },\n\n async stat(path: string): Promise<FileStat> {\n const stats = await fs.stat(path);\n return {\n isFile: () => stats.isFile(),\n isDirectory: () => stats.isDirectory(),\n size: Number(stats.size),\n mtime: new Date(stats.mtime),\n };\n },\n\n async exists(path: string): Promise<boolean> {\n try {\n await fs.stat(path);\n return true;\n } catch {\n return false;\n }\n },\n\n async writeFile(path: string, data: Buffer | string): Promise<void> {\n await fs.writeFile(path, data);\n },\n\n async appendFile(path: string, data: Buffer | string): Promise<void> {\n await fs.appendFile(path, data);\n },\n\n async mkdir(path: string, opts?: { recursive?: boolean }): Promise<void> {\n await fs.mkdir(path, opts);\n },\n\n async rm(path: string, opts?: { recursive?: boolean; force?: boolean }): Promise<void> {\n try {\n const stats = await fs.stat(path);\n if (stats.isDirectory()) {\n await fs.rmdir(path, { recursive: opts?.recursive });\n } else {\n await fs.unlink(path);\n }\n } catch (err) {\n if (!opts?.force) throw err;\n }\n },\n\n resolve(...paths: string[]): string {\n return pathModule.resolve(...paths);\n },\n\n dirname(path: string): string {\n return pathModule.dirname(path);\n },\n\n basename(path: string): string {\n return pathModule.basename(path);\n },\n\n async glob(pattern: string, opts?: { cwd?: string }): Promise<string[]> {\n const cwd = opts?.cwd ?? \"/\";\n return expandGlob(memfs, pattern, cwd);\n },\n };\n}\n\nasync function expandGlob(memfs: IFs, pattern: string, cwd: string): Promise<string[]> {\n const { promises: fs } = memfs;\n\n // Handle brace expansion first\n const patterns = expandBraces(pattern);\n const allMatches: string[] = [];\n\n for (const pat of patterns) {\n const matches = await matchPattern(fs, pat, cwd);\n allMatches.push(...matches);\n }\n\n // Remove duplicates and sort\n return [...new Set(allMatches)].sort();\n}\n\nfunction expandBraces(pattern: string): string[] {\n const braceMatch = pattern.match(/\\{([^{}]+)\\}/);\n if (!braceMatch) return [pattern];\n\n const before = pattern.slice(0, braceMatch.index);\n const after = pattern.slice(braceMatch.index! + braceMatch[0].length);\n const options = braceMatch[1]!.split(\",\");\n\n const results: string[] = [];\n for (const opt of options) {\n const expanded = expandBraces(before + opt + after);\n results.push(...expanded);\n }\n return results;\n}\n\nasync function matchPattern(\n fs: IFs[\"promises\"],\n pattern: string,\n cwd: string\n): Promise<string[]> {\n const parts = pattern.split(\"/\").filter((p) => p !== \"\");\n const isAbsolute = pattern.startsWith(\"/\");\n const startDir = isAbsolute ? \"/\" : cwd;\n\n return matchParts(fs, parts, startDir, isAbsolute);\n}\n\nasync function matchParts(\n fs: IFs[\"promises\"],\n parts: string[],\n currentPath: string,\n isAbsolute: boolean\n): Promise<string[]> {\n if (parts.length === 0) {\n return [currentPath];\n }\n\n const [part, ...rest] = parts;\n\n // Handle ** (recursive glob)\n if (part === \"**\") {\n const results: string[] = [];\n\n // Match current directory\n const withoutStar = await matchParts(fs, rest, currentPath, isAbsolute);\n results.push(...withoutStar);\n\n // Recurse into subdirectories\n try {\n const entries = await fs.readdir(currentPath);\n for (const entry of entries) {\n const entryPath = pathModule.join(currentPath, String(entry));\n try {\n const stat = await fs.stat(entryPath);\n if (stat.isDirectory()) {\n const subMatches = await matchParts(fs, parts, entryPath, isAbsolute);\n results.push(...subMatches);\n }\n } catch {\n // Skip inaccessible entries\n }\n }\n } catch {\n // Directory not readable\n }\n\n return results;\n }\n\n // Handle regular glob patterns\n const regex = globToRegex(part!);\n\n try {\n const entries = await fs.readdir(currentPath);\n const results: string[] = [];\n\n for (const entry of entries) {\n const entryName = String(entry);\n if (regex.test(entryName)) {\n const entryPath = pathModule.join(currentPath, entryName);\n if (rest.length === 0) {\n results.push(entryPath);\n } else {\n try {\n const stat = await fs.stat(entryPath);\n if (stat.isDirectory()) {\n const subMatches = await matchParts(fs, rest, entryPath, isAbsolute);\n results.push(...subMatches);\n }\n } catch {\n // Skip inaccessible entries\n }\n }\n }\n }\n\n return results;\n } catch {\n return [];\n }\n}\n\nfunction globToRegex(pattern: string): RegExp {\n let regex = \"^\";\n for (let i = 0; i < pattern.length; i++) {\n const char = pattern[i]!;\n if (char === \"*\") {\n regex += \"[^/]*\";\n } else if (char === \"?\") {\n regex += \"[^/]\";\n } else if (char === \"[\") {\n // Character class\n let j = i + 1;\n let classContent = \"\";\n while (j < pattern.length && pattern[j] !== \"]\") {\n classContent += pattern[j];\n j++;\n }\n regex += `[${classContent}]`;\n i = j;\n } else if (\".+^${}()|\\\\\".includes(char)) {\n regex += \"\\\\\" + char;\n } else {\n regex += char;\n }\n }\n regex += \"$\";\n return new RegExp(regex);\n}\n"
5
+ "import type { IFs } from \"memfs\";\nimport type { VirtualFS, FileStat } from \"../types.cjs\";\nimport * as pathModule from \"path\";\nimport { globVirtualFS } from \"../utils/glob.cjs\";\nimport {\n discardsSpecialFileWrites,\n existsSpecialFile,\n getSpecialPathError,\n readSpecialFile,\n statSpecialFile,\n} from \"./special-files.cjs\";\n\nexport function createVirtualFS(memfs: IFs): VirtualFS {\n const { promises: fs } = memfs;\n\n return {\n readFile: (async (path: string, encoding?: BufferEncoding): Promise<Buffer | string> => {\n const specialContent = readSpecialFile(path, encoding);\n if (specialContent !== undefined) return specialContent;\n const content = await fs.readFile(path);\n const buf = Buffer.from(content);\n return encoding ? buf.toString(encoding) : buf;\n }) as VirtualFS[\"readFile\"],\n\n async readdir(path: string): Promise<string[]> {\n const specialError = getSpecialPathError(path, \"readdir\");\n if (specialError) throw specialError;\n const entries = await fs.readdir(path);\n return entries.map(String);\n },\n\n async stat(path: string): Promise<FileStat> {\n const specialStat = statSpecialFile(path);\n if (specialStat) return specialStat;\n const stats = await fs.stat(path);\n return {\n isFile: () => stats.isFile(),\n isDirectory: () => stats.isDirectory(),\n size: Number(stats.size),\n mtime: new Date(stats.mtime),\n };\n },\n\n async exists(path: string): Promise<boolean> {\n const specialExists = existsSpecialFile(path);\n if (specialExists !== undefined) return specialExists;\n try {\n await fs.stat(path);\n return true;\n } catch {\n return false;\n }\n },\n\n async writeFile(path: string, data: Buffer | string): Promise<void> {\n if (discardsSpecialFileWrites(path)) {\n return;\n }\n await fs.writeFile(path, data);\n },\n\n async appendFile(path: string, data: Buffer | string): Promise<void> {\n if (discardsSpecialFileWrites(path)) {\n return;\n }\n await fs.appendFile(path, data);\n },\n\n async mkdir(path: string, opts?: { recursive?: boolean }): Promise<void> {\n const specialError = getSpecialPathError(path, \"mkdir\");\n if (specialError) throw specialError;\n await fs.mkdir(path, opts);\n },\n\n async rm(path: string, opts?: { recursive?: boolean; force?: boolean }): Promise<void> {\n const specialError = getSpecialPathError(path, \"rm\");\n if (specialError) throw specialError;\n try {\n const stats = await fs.stat(path);\n if (stats.isDirectory()) {\n await fs.rmdir(path, { recursive: opts?.recursive });\n } else {\n await fs.unlink(path);\n }\n } catch (err) {\n if (!opts?.force) throw err;\n }\n },\n\n resolve(...paths: string[]): string {\n return pathModule.resolve(...paths);\n },\n\n dirname(path: string): string {\n return pathModule.dirname(path);\n },\n\n basename(path: string): string {\n return pathModule.basename(path);\n },\n\n async glob(pattern: string, opts?: { cwd?: string }): Promise<string[]> {\n const cwd = opts?.cwd ?? \"/\";\n return globVirtualFS(\n {\n readdir: (filePath: string) => this.readdir(filePath),\n stat: (filePath: string) => this.stat(filePath),\n resolve: (...paths: string[]) => this.resolve(...paths),\n },\n pattern,\n { cwd }\n );\n },\n };\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "8EA2A9013D6F90A464756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "F25975C12E3475B564756E2164756E21",
9
9
  "names": []
10
10
  }