11import install , { Logger } from "../plumbing/install.ts"
22import useShellEnv from '../hooks/useShellEnv.ts'
33import usePantry from '../hooks/usePantry.ts'
4- import * as semver from "../utils/semver.ts"
54import hydrate from "../plumbing/hydrate.ts"
65import resolve from "../plumbing/resolve.ts"
76import { spawn } from "node:child_process"
87import useSync from "../hooks/useSync.ts"
8+ import which from "../plumbing/which.ts"
99import link from "../plumbing/link.ts"
1010import Path from "../utils/Path.ts"
1111import { isArray } from "is-what"
@@ -34,24 +34,33 @@ export default async function run(cmd: Cmd, opts: {stdout: true, status: true} &
3434export default async function run ( cmd : Cmd , opts : { stderr : true , status : true } & OptsEx ) : Promise < { stderr : string , status : number } > ;
3535export default async function run ( cmd : Cmd , opts : { stdout : true , stderr : true , status : true } & OptsEx ) : Promise < { stdout : string , stderr : string , status : number } > ;
3636export default async function run ( cmd : Cmd , opts ?: Options ) : Promise < void | { stdout ?: string | undefined ; stderr ?: string | undefined ; status ?: number | undefined ; } > {
37- const [ arg0 , [ spawn0 , args ] ] = ( ( ) => {
38- if ( isArray ( cmd ) ) {
39- if ( cmd . length == 0 ) {
40- throw new RunError ( 'EUSAGE' , `\`cmd\` evaluated empty: ${ cmd } ` )
41- }
42- const arg0 = cmd . shift ( ) ! . toString ( )
43- return [ arg0 , [ arg0 , cmd . map ( x => x . toString ( ) ) ] ]
44- } else {
37+
38+ const { usesh, arg0 : whom } = ( ( ) => {
39+ if ( ! isArray ( cmd ) ) {
4540 const s = cmd . trim ( )
4641 const i = s . indexOf ( ' ' )
47- return [ s . slice ( 0 , i ) , [ '/bin/sh' , [ '-c' , cmd ] ] ]
42+ const usesh = i >= 0
43+ const arg0 = s . slice ( 0 , i )
44+ cmd = s . slice ( i + 1 )
45+ return { usesh, arg0 }
46+ } else if ( cmd . length == 0 ) {
47+ throw new RunError ( 'EUSAGE' , `\`cmd\` evaluated empty: ${ cmd } ` )
48+ } else {
49+ return {
50+ usesh : false ,
51+ arg0 : cmd . shift ( ) ! . toString ( ) . trim ( )
52+ }
4853 }
4954 } ) ( )
5055
51- const env = await setup ( arg0 , opts ?. env ?? Deno . env . toObject ( ) , opts ?. logger )
56+ const { env, shebang } = await setup ( whom , opts ?. env ?? Deno . env . toObject ( ) , opts ?. logger )
57+ const arg0 = usesh ? '/bin/sh' : shebang . shift ( ) !
58+ const args = usesh
59+ ? [ '-c' , `${ shebang . join ( ' ' ) } ${ cmd } ` ]
60+ : [ ...shebang , ...( cmd as ( string | Path ) [ ] ) . map ( x => x . toString ( ) ) ]
5261
5362 return new Promise ( ( resolve , reject ) => {
54- const proc = spawn ( spawn0 , args , {
63+ const proc = spawn ( arg0 , args , {
5564 env,
5665 stdio : [
5766 "pipe" ,
@@ -84,18 +93,10 @@ async function setup(cmd: string, env: Record<string, string | undefined>, logge
8493 await useSync ( )
8594 }
8695
87- const project = await ( async ( ) => {
88- for await ( const { project } of pantry . ls ( ) ) {
89- const provides = await pantry . project ( project ) . provides ( )
90- //TODO handle eg. node^16 here
91- if ( provides . includes ( cmd ) ) {
92- return project
93- }
94- }
95- throw new RunError ( 'ENOENT' , `No project in pantry provides ${ cmd } ` )
96- } ) ( )
96+ const wut = await which ( cmd )
97+ if ( ! wut ) throw new RunError ( 'ENOENT' , `No project in pantry provides ${ cmd } ` )
9798
98- const { pkgs } = await hydrate ( { project , constraint : new semver . Range ( '*' ) } )
99+ const { pkgs } = await hydrate ( wut )
99100 const { pending, installed } = await resolve ( pkgs )
100101 for ( const pkg of pending ) {
101102 const installation = await install ( pkg , logger )
@@ -115,7 +116,7 @@ async function setup(cmd: string, env: Record<string, string | undefined>, logge
115116 }
116117 }
117118
118- return sh . flatten ( pkgenv )
119+ return { env : sh . flatten ( pkgenv ) , shebang : wut . shebang }
119120}
120121
121122
0 commit comments