11import { access , readFile , readdir } from "node:fs/promises"
22import { dirname , extname , join } from "node:path"
3- import { createAppPathCache , forgetAppPath , getAppPath , rememberAppPath } from "../app-path-cache"
3+ import { APP_PATH_CACHE_LIMIT , createAppPathCache , forgetAppPath , getAppPath , rememberAppPath } from "../app-path-cache"
44import { execFileHidden } from "./child-process"
55
66const exists = ( path : string ) =>
@@ -10,13 +10,24 @@ const exists = (path: string) =>
1010
1111const windowsAppPathCache = createAppPathCache ( )
1212
13+ // This scan runs after direct executable and command shim resolution, so keep it at app path cache size.
14+ const WINDOWS_APP_FALLBACK_DIR_LIMIT = APP_PATH_CACHE_LIMIT
15+
1316const searchKey = ( value : string ) =>
1417 value
1518 . split ( "" )
1619 . filter ( ( value : string ) => / [ a - z 0 - 9 ] / i. test ( value ) )
1720 . map ( ( value : string ) => value . toLowerCase ( ) )
1821 . join ( "" )
1922
23+ export function getWindowsFallbackSearchDirs ( paths : string [ ] ) {
24+ return Array . from (
25+ new Set ( paths . flatMap ( ( path ) => [ dirname ( path ) , dirname ( dirname ( path ) ) , dirname ( dirname ( dirname ( path ) ) ) ] ) ) ,
26+ )
27+ . filter ( ( dir ) => dir !== "." )
28+ . slice ( 0 , WINDOWS_APP_FALLBACK_DIR_LIMIT )
29+ }
30+
2031export function checkAppExists ( appName : string ) {
2132 if ( process . platform === "win32" ) return true
2233 if ( process . platform === "linux" ) return true
@@ -133,20 +144,17 @@ async function resolveWindowsAppPath(appName: string): Promise<string | null> {
133144 const key = searchKey ( appName )
134145
135146 if ( key ) {
136- for ( const path of paths ) {
137- const dirs = [ dirname ( path ) , dirname ( dirname ( path ) ) , dirname ( dirname ( dirname ( path ) ) ) ]
138- for ( const dir of dirs ) {
139- try {
140- for ( const entry of await readdir ( dir ) ) {
141- const candidate = join ( dir , entry )
142- if ( ! hasExt ( candidate , "exe" ) ) continue
143- const stem = entry . replace ( / \. e x e $ / i, "" )
144- const name = searchKey ( stem )
145- if ( name . includes ( key ) || key . includes ( name ) ) return remember ( candidate )
146- }
147- } catch {
148- continue
147+ for ( const dir of getWindowsFallbackSearchDirs ( paths ) ) {
148+ try {
149+ for ( const entry of await readdir ( dir ) ) {
150+ const candidate = join ( dir , entry )
151+ if ( ! hasExt ( candidate , "exe" ) ) continue
152+ const stem = entry . replace ( / \. e x e $ / i, "" )
153+ const name = searchKey ( stem )
154+ if ( name . includes ( key ) || key . includes ( name ) ) return remember ( candidate )
149155 }
156+ } catch {
157+ continue
150158 }
151159 }
152160 }
0 commit comments