|
1 | 1 | import { globby, Options } from "globby" |
2 | | -import os from "os" |
| 2 | +import * as os from "os" |
3 | 3 | import * as path from "path" |
4 | 4 | import { arePathsEqual } from "@utils/path" |
5 | 5 |
|
6 | | -export async function listFiles(dirPath: string, recursive: boolean, limit: number): Promise<[string[], boolean]> { |
7 | | - // First resolve the path normally - path.resolve doesn't care about glob special characters |
8 | | - const absolutePath = path.resolve(dirPath) |
9 | | - // Do not allow listing files in root or home directory, which cline tends to want to do when the user's prompt is vague. |
| 6 | +// Constants |
| 7 | +const DEFAULT_IGNORE_DIRECTORIES = [ |
| 8 | + "node_modules", |
| 9 | + "__pycache__", |
| 10 | + "env", |
| 11 | + "venv", |
| 12 | + "target/dependency", |
| 13 | + "build/dependencies", |
| 14 | + "dist", |
| 15 | + "out", |
| 16 | + "bundle", |
| 17 | + "vendor", |
| 18 | + "tmp", |
| 19 | + "temp", |
| 20 | + "deps", |
| 21 | + "pkg", |
| 22 | + "Pods", |
| 23 | +] |
| 24 | + |
| 25 | +// Helper functions |
| 26 | +function isRestrictedPath(absolutePath: string): boolean { |
10 | 27 | const root = process.platform === "win32" ? path.parse(absolutePath).root : "/" |
11 | 28 | const isRoot = arePathsEqual(absolutePath, root) |
12 | 29 | if (isRoot) { |
13 | | - return [[root], false] |
| 30 | + return true |
14 | 31 | } |
| 32 | + |
15 | 33 | const homeDir = os.homedir() |
16 | 34 | const isHomeDir = arePathsEqual(absolutePath, homeDir) |
17 | 35 | if (isHomeDir) { |
18 | | - return [[homeDir], false] |
| 36 | + return true |
| 37 | + } |
| 38 | + |
| 39 | + return false |
| 40 | +} |
| 41 | + |
| 42 | +function isTargetingHiddenDirectory(absolutePath: string): boolean { |
| 43 | + const dirName = path.basename(absolutePath) |
| 44 | + return dirName.startsWith(".") |
| 45 | +} |
| 46 | + |
| 47 | +function buildIgnorePatterns(absolutePath: string): string[] { |
| 48 | + const isTargetHidden = isTargetingHiddenDirectory(absolutePath) |
| 49 | + |
| 50 | + const patterns = [...DEFAULT_IGNORE_DIRECTORIES] |
| 51 | + |
| 52 | + // Only ignore hidden directories if we're not explicitly targeting a hidden directory |
| 53 | + if (!isTargetHidden) { |
| 54 | + patterns.push(".*") |
19 | 55 | } |
20 | 56 |
|
21 | | - const dirsToIgnore = [ |
22 | | - "node_modules", |
23 | | - "__pycache__", |
24 | | - "env", |
25 | | - "venv", |
26 | | - "target/dependency", |
27 | | - "build/dependencies", |
28 | | - "dist", |
29 | | - "out", |
30 | | - "bundle", |
31 | | - "vendor", |
32 | | - "tmp", |
33 | | - "temp", |
34 | | - "deps", |
35 | | - "pkg", |
36 | | - "Pods", |
37 | | - ".*", // '!**/.*' excludes hidden directories, while '!**/.*/**' excludes only their contents. This way we are at least aware of the existence of hidden directories. |
38 | | - ].map((dir) => `**/${dir}/**`) |
| 57 | + return patterns.map((dir) => `**/${dir}/**`) |
| 58 | +} |
| 59 | + |
| 60 | +export async function listFiles(dirPath: string, recursive: boolean, limit: number): Promise<[string[], boolean]> { |
| 61 | + const absolutePath = path.resolve(dirPath) |
| 62 | + |
| 63 | + // Do not allow listing files in root or home directory |
| 64 | + if (isRestrictedPath(absolutePath)) { |
| 65 | + return [[], false] |
| 66 | + } |
39 | 67 |
|
40 | 68 | const options: Options = { |
41 | 69 | cwd: dirPath, |
42 | 70 | dot: true, // do not ignore hidden files/directories |
43 | 71 | absolute: true, |
44 | | - markDirectories: true, // Append a / on any directories matched (/ is used on windows as well, so dont use path.sep) |
| 72 | + markDirectories: true, // Append a / on any directories matched |
45 | 73 | gitignore: recursive, // globby ignores any files that are gitignored |
46 | | - ignore: recursive ? dirsToIgnore : undefined, // just in case there is no gitignore, we ignore sensible defaults |
47 | | - onlyFiles: false, // true by default, false means it will list directories on their own too |
| 74 | + ignore: recursive ? buildIgnorePatterns(absolutePath) : undefined, |
| 75 | + onlyFiles: false, // include directories in results |
48 | 76 | suppressErrors: true, |
49 | 77 | } |
50 | 78 |
|
51 | | - // * globs all files in one dir, ** globs files in nested directories |
52 | | - // For non-recursive listing, we still use a simple pattern |
53 | 79 | const filePaths = recursive ? await globbyLevelByLevel(limit, options) : (await globby("*", options)).slice(0, limit) |
54 | 80 |
|
55 | 81 | return [filePaths, filePaths.length >= limit] |
|
0 commit comments