@@ -5,13 +5,19 @@ import {
55 spawnSync ,
66 type StdioOptions ,
77} from "node:child_process" ;
8- import { statSync } from "node:fs" ;
98import { extname , join } from "node:path" ;
109
1110import type { ServerRuntimeEnvironment } from "@t3tools/contracts" ;
1211import { Effect , Exit , Scope } from "effect" ;
1312import { ChildProcess , ChildProcessSpawner } from "effect/unstable/process" ;
1413
14+ import {
15+ isExecutableFile ,
16+ resolveCommandCandidates ,
17+ resolvePathEnvironmentVariable ,
18+ resolveWindowsPathExtensions ,
19+ stripWrappingQuotes ,
20+ } from "./commandResolution" ;
1521import { detectServerRuntimeEnvironment } from "./runtimeEnvironment" ;
1622
1723interface ProcessSpawnBaseOptions {
@@ -93,71 +99,6 @@ function resolveRuntimeEnvironment(
9399 return runtimeEnvironment ?? detectServerRuntimeEnvironment ( ) ;
94100}
95101
96- function resolvePathEnvironmentVariable ( env : NodeJS . ProcessEnv ) : string {
97- return env . PATH ?? env . Path ?? env . path ?? "" ;
98- }
99-
100- function resolveWindowsPathExtensions ( env : NodeJS . ProcessEnv ) : ReadonlyArray < string > {
101- const rawValue = env . PATHEXT ;
102- const fallback = [ ".COM" , ".EXE" , ".BAT" , ".CMD" ] ;
103- if ( ! rawValue ) return fallback ;
104-
105- const parsed = rawValue
106- . split ( ";" )
107- . map ( ( entry ) => entry . trim ( ) )
108- . filter ( ( entry ) => entry . length > 0 )
109- . map ( ( entry ) => ( entry . startsWith ( "." ) ? entry . toUpperCase ( ) : `.${ entry . toUpperCase ( ) } ` ) ) ;
110- return parsed . length > 0 ? Array . from ( new Set ( parsed ) ) : fallback ;
111- }
112-
113- function resolveCommandCandidates (
114- command : string ,
115- windowsPathExtensions : ReadonlyArray < string > ,
116- ) : ReadonlyArray < string > {
117- const extension = extname ( command ) ;
118- const normalizedExtension = extension . toUpperCase ( ) ;
119-
120- if ( extension . length > 0 && windowsPathExtensions . includes ( normalizedExtension ) ) {
121- const commandWithoutExtension = command . slice ( 0 , - extension . length ) ;
122- return Array . from (
123- new Set ( [
124- command ,
125- `${ commandWithoutExtension } ${ normalizedExtension } ` ,
126- `${ commandWithoutExtension } ${ normalizedExtension . toLowerCase ( ) } ` ,
127- ] ) ,
128- ) ;
129- }
130-
131- const candidates : string [ ] = [ command ] ;
132- for ( const candidateExtension of windowsPathExtensions ) {
133- candidates . push ( `${ command } ${ candidateExtension } ` ) ;
134- candidates . push ( `${ command } ${ candidateExtension . toLowerCase ( ) } ` ) ;
135- }
136- return Array . from ( new Set ( candidates ) ) ;
137- }
138-
139- function stripWrappingQuotes ( value : string ) : string {
140- return value . replace ( / ^ " + | " + $ / g, "" ) ;
141- }
142-
143- function isExecutableFile (
144- filePath : string ,
145- windowsPathExtensions : ReadonlyArray < string > ,
146- ) : boolean {
147- try {
148- const stat = statSync ( filePath ) ;
149- if ( ! stat . isFile ( ) ) return false ;
150- const extension = extname ( filePath ) ;
151- if ( extension . length === 0 ) return false ;
152- if ( windowsPathExtensions . length === 0 ) {
153- return true ;
154- }
155- return windowsPathExtensions . includes ( extension . toUpperCase ( ) ) ;
156- } catch {
157- return false ;
158- }
159- }
160-
161102function resolveEffectiveEnvironment ( options : ProcessLaunchPlanOptions ) : NodeJS . ProcessEnv {
162103 const env = ( options . env ?? { } ) as NodeJS . ProcessEnv ;
163104 if ( options . inheritParentEnv === false ) {
@@ -175,7 +116,7 @@ function resolveWindowsCommand(
175116 env : NodeJS . ProcessEnv ,
176117) : ResolvedWindowsCommand | null {
177118 const windowsPathExtensions = resolveWindowsPathExtensions ( env ) ;
178- const candidates = resolveCommandCandidates ( command , windowsPathExtensions ) ;
119+ const candidates = resolveCommandCandidates ( command , "win32" , windowsPathExtensions ) ;
179120
180121 const classify = ( filePath : string ) : ResolvedWindowsCommand => {
181122 const extension = extname ( filePath ) . toUpperCase ( ) ;
@@ -187,7 +128,7 @@ function resolveWindowsCommand(
187128
188129 if ( command . includes ( "/" ) || command . includes ( "\\" ) ) {
189130 for ( const candidate of candidates ) {
190- if ( isExecutableFile ( candidate , windowsPathExtensions ) ) {
131+ if ( isExecutableFile ( candidate , "win32" , windowsPathExtensions ) ) {
191132 return classify ( candidate ) ;
192133 }
193134 }
@@ -202,7 +143,7 @@ function resolveWindowsCommand(
202143 for ( const pathEntry of pathEntries ) {
203144 for ( const candidate of candidates ) {
204145 const candidatePath = join ( pathEntry , candidate ) ;
205- if ( isExecutableFile ( candidatePath , windowsPathExtensions ) ) {
146+ if ( isExecutableFile ( candidatePath , "win32" , windowsPathExtensions ) ) {
206147 return classify ( candidatePath ) ;
207148 }
208149 }
0 commit comments