Skip to content

Commit 9d5b3e4

Browse files
authored
feat: enhance support for multiline cmd args (google#753)
* feat: enhance support for multiline cmd args closes google#539 * chore: fix `raw` ref in tpl pieces normalizer * chore: lint
1 parent fe24b7a commit 9d5b3e4

File tree

7 files changed

+50
-6
lines changed

7 files changed

+50
-6
lines changed

package-lock.json

+4-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
"@types/node": ">=20.11.30",
6060
"@types/which": "^3.0.3",
6161
"@webpod/ps": "^0.0.0-beta.2",
62+
"@webpod/ingrid": "^0.0.0-beta.3",
6263
"c8": "^9.1.0",
6364
"chalk": "^5.3.0",
6465
"depseek": "^0.4.1",

src/core.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
formatCmd,
3535
getCallerLocation,
3636
noop,
37+
normalizeMultilinePieces,
3738
parseDuration,
3839
quote,
3940
quotePowerShell,
@@ -137,10 +138,9 @@ export const $: Shell & Options = new Proxy<Shell & Options>(
137138
const promise = new ProcessPromise((...args) => ([resolve, reject] = args))
138139
const cmd = buildCmd(
139140
$.quote,
140-
pieces as TemplateStringsArray,
141+
normalizeMultilinePieces(pieces as TemplateStringsArray),
141142
args
142143
) as string
143-
144144
const snapshot = getStore()
145145
const sync = snapshot[syncExec]
146146
const callback = () => promise.isHalted || promise.run()

src/util.ts

+19-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
import { chalk } from './vendor.js'
15+
import { chalk, parseLine } from './vendor.js'
1616

1717
export function noop() {}
1818

@@ -24,6 +24,24 @@ export function isString(obj: any) {
2424
return typeof obj === 'string'
2525
}
2626

27+
export function normalizeMultilinePieces(
28+
pieces: TemplateStringsArray
29+
): TemplateStringsArray {
30+
return Object.assign(
31+
pieces.map((p, i) =>
32+
p.trim()
33+
? parseLine(p)
34+
.words.map(({ w, e }) => {
35+
if (w === '\\') return ''
36+
return w.trim() + (p[e + 1] === ' ' ? ' ' : '')
37+
})
38+
.join(' ')
39+
: pieces[i]
40+
),
41+
{ raw: pieces.raw }
42+
)
43+
}
44+
2745
export function quote(arg: string) {
2846
if (/^[a-z0-9/_.\-@:=]+$/i.test(arg) || arg === '') {
2947
return arg

src/vendor.ts

+1
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,4 @@ export { default as chalk, type ChalkInstance } from 'chalk'
5656
export { default as which } from 'which'
5757
export { default as minimist } from 'minimist'
5858
export { default as ps } from '@webpod/ps'
59+
export { parseLine } from '@webpod/ingrid'

test/core.test.js

+22
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,28 @@ describe('core', () => {
4848
assert.equal((await $`echo -n ${''}`).toString(), '')
4949
})
5050

51+
test('handles multiline literals', async () => {
52+
assert.equal(
53+
(
54+
await $`echo foo
55+
bar
56+
"baz
57+
qux"
58+
`
59+
).toString(),
60+
'foo bar baz\n qux\n'
61+
)
62+
assert.equal(
63+
(
64+
await $`echo foo \
65+
bar \
66+
baz \
67+
`
68+
).toString(),
69+
'foo bar baz\n'
70+
)
71+
})
72+
5173
test('can create a dir with a space in the name', async () => {
5274
let name = 'foo bar'
5375
try {

test/fixtures/js-project/package-lock.json

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)