dax

Cross-platform shell tools for Deno and Node.js inspired by zx.

README

dax


Cross-platform shell tools for Deno and Node.js inspired by zx.

Differences with zx


1. Cross-platform shell.
   - Makes more code work on Windows.
   - Allows exporting the shell's environment to the current process.
   - Uses deno_task_shell's parser.
   - Has common commands built-in for better Windows support.
1. Minimal globals or global configuration.
   - Only a default instance of $, but it's not mandatory to use this.
1. No custom CLI.
1. Good for application code in addition to use as a shell script replacement.
1. Named after my cat.

Executing commands


  1. ```ts
  2. #!/usr/bin/env -S deno run --allow-all
  3. import $ from "https://deno.land/x/dax/mod.ts";

  4. // run a command
  5. await $`echo 5`; // outputs: 5

  6. // outputting to stdout and running a sub process
  7. await $`echo 1 && deno run main.ts`;

  8. // parallel
  9. await Promise.all([
  10.   $`sleep 1 ; echo 1`,
  11.   $`sleep 2 ; echo 2`,
  12.   $`sleep 3 ; echo 3`,
  13. ]);
  14. ```

Note: Above instructions are for Deno. For Node.js, install via npm install --save-dev dax-sh then import via import $ from "dax-sh";.

Getting output


Get the stdout of a command (makes stdout "quiet"):

  1. ```ts
  2. const result = await $`echo 1`.text();
  3. console.log(result); // 1
  4. ```

Get the result of stdout as json (makes stdout "quiet"):

  1. ```ts
  2. const result = await $`echo '{ "prop": 5 }'`.json();
  3. console.log(result.prop); // 5
  4. ```

Get the result of stdout as bytes (makes stdout "quiet"):

  1. ```ts
  2. const bytes = await $`gzip < file.txt`.bytes();
  3. console.log(bytes);
  4. ```

Get the result of stdout as a list of lines (makes stdout "quiet"):

  1. ```ts
  2. const result = await $`echo 1 && echo 2`.lines();
  3. console.log(result); // ["1", "2"]
  4. ```

Working with a lower level result that provides more details:

  1. ```ts
  2. const result = await $`deno eval 'console.log(1); console.error(2);'`
  3.   .stdout("piped")
  4.   .stderr("piped");
  5. console.log(result.code); // 0
  6. console.log(result.stdoutBytes); // Uint8Array(2) [ 49, 10 ]
  7. console.log(result.stdout); // 1\n
  8. console.log(result.stderr); // 2\n
  9. const output = await $`echo '{ "test": 5 }'`.stdout("piped");
  10. console.log(output.stdoutJson);
  11. ```

Getting the combined output:

  1. ```ts
  2. const result = await $`deno eval 'console.log(1); console.error(2); console.log(3);'`
  3.   .captureCombined();

  4. console.log(result.combined); // 1\n2\n3\n
  5. ```

Piping


Piping stdout or stderr to a Deno.WriterSync:

  1. ```ts
  2. await $`echo 1`.stdout(Deno.stderr);
  3. await $`deno eval 'console.error(2);`.stderr(Deno.stdout);
  4. ```

Piping to a WritableStream:

  1. ```ts
  2. await $`echo 1`.stdout(Deno.stderr.writable, { preventClose: true });
  3. // or with a redirect
  4. await $`echo 1 > ${someWritableStream}`;
  5. ```

To a file path:

  1. ```ts
  2. await $`echo 1`.stdout($.path("data.txt"));
  3. // or
  4. await $`echo 1 > data.txt`;
  5. // or
  6. await $`echo 1 > ${$.path("data.txt")}`;
  7. ```

To a file:

  1. ```ts
  2. using file = $.path("data.txt").openSync({ write: true, create: true });
  3. await $`echo 1`.stdout(file);
  4. // or
  5. await $`echo 1 > ${file}`;
  6. ```

From one command to another:

  1. ```ts
  2. const output = await $`echo foo && echo bar`
  3.   .pipe($`grep foo`)
  4.   .text();

  5. // or using a pipe sequence
  6. const output = await $`(echo foo && echo bar) | grep foo`
  7.   .text();
  8. ```

Providing arguments to a command


Use an expression in a template literal to provide a single argument to a command:

  1. ```ts
  2. const dirName = "some_dir";
  3. await $`mkdir ${dirName}`; // executes as: mkdir some_dir
  4. ```

Arguments are escaped so strings with spaces get escaped and remain as a single argument:

  1. ```ts
  2. const dirName = "Dir with spaces";
  3. await $`mkdir ${dirName}`; // executes as: mkdir 'Dir with spaces'
  4. ```

Alternatively, provide an array for multiple arguments:

  1. ```ts
  2. const dirNames = ["some_dir", "other dir"];
  3. await $`mkdir ${dirNames}`; // executes as: mkdir some_dir 'other dir'
  4. ```

If you do not want to escape arguments in a template literal, you can opt out completely, by using $.raw:

  1. ```ts
  2. const args = "arg1   arg2   arg3";
  3. await $.raw`echo ${args}`; // executes as: echo arg1   arg2   arg3
  4. ```

Providing stdout of one command to another is possible as follows:

  1. ```ts
  2. // Note: This will read trim the last newline of the other command's stdout
  3. const result = await $`echo 1`.stdout("piped"); // need to set stdout as piped for this to work
  4. const finalText = await $`echo ${result}`.text();
  5. console.log(finalText); // 1
  6. ```

...though it's probably more straightforward to just collect the output text of a command and provide that:

  1. ```ts
  2. const result = await $`echo 1`.text();
  3. const finalText = await $`echo ${result}`.text();
  4. console.log(finalText); // 1
  5. ```