@@ -3,6 +3,7 @@ import fse from 'fs-extra'
3
3
import path from 'path'
4
4
import crypto from 'crypto'
5
5
import chalk from 'chalk'
6
+ import { quote } from 'shell-quote'
6
7
import { closest } from 'fastest-levenshtein'
7
8
import { findMonorepoRoot } from 'find-monorepo-root'
8
9
import { spawn } from './spawn.js'
@@ -18,7 +19,46 @@ async function ensureTargetDir(root) {
18
19
return targetDir
19
20
}
20
21
21
- async function makeExecFile ( root , task , cmd , args ) {
22
+ function makeExecMain ( cmd , args ) {
23
+ const usedPos = [ ]
24
+ const defaults = [ ]
25
+ const main = cmd
26
+ // collect default values
27
+ . replace ( / \{ ( [ 1 - 9 ] \d ? ) = ( [ ^ } ] + ) } / g, ( whole , id , defaultValue ) => {
28
+ const pos = id - 1
29
+ defaults [ pos ] = defaultValue
30
+ return `{${ id } }`
31
+ } )
32
+ // replace placeholder
33
+ . replace ( / \{ ( [ * @ ] | [ 1 - 9 ] \d ? ) } / g, ( whole , id ) => {
34
+ if ( id === "@" || id === '*' ) {
35
+ usedPos . push ( ...args . map ( ( _ , i ) => i ) )
36
+ return id === '@' ? quote ( args ) : quote ( [ args . join ( ' ' ) ] )
37
+ }
38
+
39
+
40
+ const pos = id - 1
41
+ if ( pos >= 0 && pos < args . length ) {
42
+ usedPos . push ( pos )
43
+
44
+ const arg = args [ pos ]
45
+ if ( arg && arg . trim ( ) ) {
46
+ return quote ( [ arg ] )
47
+ }
48
+ }
49
+
50
+ if ( defaults [ pos ] != null ) {
51
+ return quote ( [ defaults [ pos ] ] )
52
+ }
53
+
54
+ return ''
55
+ } )
56
+
57
+ const others = args . filter ( ( _ , i ) => ! usedPos . includes ( i ) )
58
+ return `${ main } ${ quote ( others ) } `
59
+ }
60
+
61
+ async function makeExecFile ( root , task , main ) {
22
62
const targetDir = await ensureTargetDir ( root )
23
63
24
64
const win32 = process . platform === 'win32'
@@ -35,7 +75,6 @@ async function makeExecFile(root, task, cmd, args) {
35
75
} while ( exist )
36
76
37
77
38
- const main = args . length ? `${ cmd } ${ args . join ( ' ' ) } ` : cmd
39
78
const declare = win32 ? '@ECHO OFF' : '#!/usr/bin/env sh'
40
79
41
80
await fse . writeFile ( filePath , `${ declare } \n\n${ main } ` )
@@ -71,6 +110,7 @@ export async function run(task, args, options) {
71
110
72
111
if ( cmd ) {
73
112
const isFile = typeof cmd === 'object' && cmd . file != null
113
+ const main = isFile ? cmd . file : makeExecMain ( cmd , args )
74
114
75
115
let log = '> '
76
116
const eventColor = closest ( task , [
@@ -83,18 +123,18 @@ export async function run(task, args, options) {
83
123
] )
84
124
log += chalk [ eventColor ] ( `[${ task } ]` )
85
125
log += ' '
86
- log += isFile ? cmd . file : ` ${ cmd } `
126
+ log += main
87
127
console . log ( log )
88
128
89
129
if ( ! options . dryRun ) {
90
130
const exe = isFile
91
- ? fixFilePath ( root , cmd . file )
92
- : await makeExecFile ( root , task , cmd , args )
131
+ ? fixFilePath ( root , main )
132
+ : await makeExecFile ( root , task , main )
93
133
94
134
const child = spawn ( exe , isFile ? args : [ ] , { stdio : 'inherit' } )
95
135
96
136
if ( ! isFile ) {
97
- child . on ( 'exit' , ( ) => { fse . rmSync ( exe ) } )
137
+ child . on ( 'exit' , ( ) => { fse . rm ( exe ) } )
98
138
}
99
139
}
100
140
} else {
0 commit comments