|
1 | 1 | // @effect-diagnostics nodeBuiltinImport:off |
| 2 | +import type { Dirent } from "node:fs"; |
2 | 3 | import * as NodeFSP from "node:fs/promises"; |
3 | 4 | import * as NodeOS from "node:os"; |
4 | 5 |
|
@@ -60,6 +61,41 @@ export class WorkspaceEntries extends Context.Service< |
60 | 61 | } |
61 | 62 | >()("t3/workspace/WorkspaceEntries") {} |
62 | 63 |
|
| 64 | +const WINDOWS_LEGACY_PROFILE_JUNCTION_NAMES = new Set([ |
| 65 | + "Application Data", |
| 66 | + "Cookies", |
| 67 | + "Local Settings", |
| 68 | + "My Documents", |
| 69 | + "NetHood", |
| 70 | + "PrintHood", |
| 71 | + "Recent", |
| 72 | + "SendTo", |
| 73 | + "Start Menu", |
| 74 | + "Templates", |
| 75 | +]); |
| 76 | + |
| 77 | +async function isDirectoryEntry( |
| 78 | + dirent: Dirent, |
| 79 | + fullPath: string, |
| 80 | + platform: NodeJS.Platform, |
| 81 | +): Promise<boolean> { |
| 82 | + if (dirent.isDirectory()) { |
| 83 | + return true; |
| 84 | + } |
| 85 | + if (!dirent.isSymbolicLink()) { |
| 86 | + return false; |
| 87 | + } |
| 88 | + if (platform === "win32" && WINDOWS_LEGACY_PROFILE_JUNCTION_NAMES.has(dirent.name)) { |
| 89 | + return false; |
| 90 | + } |
| 91 | + |
| 92 | + try { |
| 93 | + return (await NodeFSP.stat(fullPath)).isDirectory(); |
| 94 | + } catch { |
| 95 | + return false; |
| 96 | + } |
| 97 | +} |
| 98 | + |
63 | 99 | function expandHomePath(input: string, path: Path.Path): string { |
64 | 100 | if (input === "~") { |
65 | 101 | return NodeOS.homedir(); |
@@ -103,6 +139,7 @@ const resolveBrowseTarget = ( |
103 | 139 |
|
104 | 140 | const make = Effect.gen(function* () { |
105 | 141 | const path = yield* Path.Path; |
| 142 | + const platform = yield* HostProcessPlatform; |
106 | 143 | const workspacePaths = yield* WorkspacePaths.WorkspacePaths; |
107 | 144 | const workspaceSearchIndexes = yield* WorkspaceSearchIndex.WorkspaceSearchIndexMap; |
108 | 145 |
|
@@ -177,23 +214,28 @@ const make = Effect.gen(function* () { |
177 | 214 |
|
178 | 215 | const showHidden = endsWithSeparator || prefix.startsWith("."); |
179 | 216 | const lowerPrefix = prefix.toLowerCase(); |
180 | | - const entries: Array<{ readonly name: string; readonly fullPath: string }> = []; |
181 | | - for (const dirent of dirents) { |
182 | | - if ( |
183 | | - dirent.isDirectory() && |
184 | | - dirent.name.toLowerCase().startsWith(lowerPrefix) && |
185 | | - (showHidden || !dirent.name.startsWith(".")) |
186 | | - ) { |
187 | | - entries.push({ |
188 | | - name: dirent.name, |
189 | | - fullPath: path.join(parentPath, dirent.name), |
190 | | - }); |
191 | | - } |
192 | | - } |
| 217 | + const entries = yield* Effect.forEach( |
| 218 | + dirents, |
| 219 | + (dirent) => |
| 220 | + Effect.promise(async () => { |
| 221 | + const fullPath = path.join(parentPath, dirent.name); |
| 222 | + if ( |
| 223 | + !dirent.name.toLowerCase().startsWith(lowerPrefix) || |
| 224 | + (!showHidden && dirent.name.startsWith(".")) || |
| 225 | + !(await isDirectoryEntry(dirent, fullPath, platform)) |
| 226 | + ) { |
| 227 | + return null; |
| 228 | + } |
| 229 | + return { name: dirent.name, fullPath }; |
| 230 | + }), |
| 231 | + { concurrency: 16 }, |
| 232 | + ); |
193 | 233 |
|
194 | 234 | return { |
195 | 235 | parentPath, |
196 | | - entries: entries.toSorted((left, right) => left.name.localeCompare(right.name)), |
| 236 | + entries: entries |
| 237 | + .filter((entry): entry is NonNullable<typeof entry> => entry !== null) |
| 238 | + .toSorted((left, right) => left.name.localeCompare(right.name)), |
197 | 239 | }; |
198 | 240 | }, |
199 | 241 | ); |
|
0 commit comments