dsherret / dax

Cross-platform shell tools for Deno and Node.js inspired by zx.
MIT License
1.06k stars 35 forks source link

how to concat fragments of a command string without using $.raw #284

Open andykais opened 2 months ago

andykais commented 2 months ago

Hi there, I am getting used to using dax for testing a cli tool I am building in deno. Usage of that cli might look like forager search --tag foobar. Generally this means I want to have test scripts run something like so:

const cli_entrypoint = path.join(path.resolve(import.meta.dirname, '..'), 'src/cli.ts')
await $`deno run ${cli_entrypoint} -A --unstable-ffi search --tag foobar`

This is a little verbose though, so I have trying to come up with a way to shorthand the first bit of boilerplate in that command into something like so:

const cli_entrypoint = path.join(path.resolve(import.meta.dirname, '..'), 'src/cli.ts')
const FORAGER_BIN = `deno run ${cli_entrypoint} -A --unstable-ffi`
await $`${FORAGER_BIN} search --tag foobar`

this doesnt seem to be a viable option however, and I get errors like this from dax:

dax: deno run --check -A --unstable-ffi /Users/andrew.kaiser/Code/andykais/forager/monorepo/packages/cli/src/cli.ts: command not found

I have had success using $.raw like so:

const cli_entrypoint = path.join(path.resolve(import.meta.dirname, '..'), 'src/cli.ts')
const FORAGER_BIN = `deno run ${cli_entrypoint} -A --unstable-ffi`
await $.raw`${FORAGER_BIN} search --tag foobar`

however I do like the auto-escape behavior dax provides, since this is another common command I want to run inside tests:

await $.raw`${FORAGER_BIN} delete --filepath ${$.escapeArg(test_resources.somefile)}`

and that feels a tag verbose. Generally I am hoping there is a way to tell dax to not escape that first part of the command, or use some kind of builder pattern for dax that makes this more practical. This may not be possible but I figured its worth asking!

andykais commented 2 months ago

update: I do seem to have found a way to get this working with the following code:

function forager_cli(strings: TemplateStringsArray, ...params: any[]) {
  const cli_entrypoint = path.join(path.resolve(import.meta.dirname!, '..'), 'src/cli.ts')
  const forager_bin = `deno run --check -A --unstable-ffi ${$.escapeArg(cli_entrypoint)}`
  let command_string = ''
  for (let index = 0; index < strings.length - 1; index++) {
    const string_part = strings[index]
    const param = params[index]
    command_string += string_part + $.escapeArg(param)
  }
  command_string += strings.at(-1)
  return $.raw`${forager_bin} ${command_string}`.stdout('piped')
}

// usage
await forager_cli`create ${test_resources.somefile} --tag=wallpaper`

const output = forager_cli`--log-level=json search --tag wallpaper`.json()

Ill leave this open for a day or two for a maintainer to peak at, but my use case is handled!