Skip to main content
Deno 2 is finally here 🎉️
Learn more

dx: A tool/runner for writing better scripts

dx is a tool and task runner for writing better scripts with Deno, and origin idea is from https://github.com/google/zx/.

why a dx instead of Google zx

dx is based on Deno and with following pros:

  • TypeScript friendly
  • Task runner support: Taskfile.ts/Taskfile.js to manage tasks
  • Easy to import third party modules, just import {red, green} from "https://deno.land/std@0.97.0/fmt/colors.ts", no idea about zx to import third party npm(package.json???)
  • More features: alias, export, $a for async iterable line output, file globs, .env support etc
  • I ❤️ 🦕

Install

deno install -q -A --unstable --no-check -r -f -n dx https://deno.land/x/deno_dx/cli.ts

Get started

Create a demo.ts file with following code:

#!/usr/bin/env dx
import {$, nothrow, cd, pwd, question, os, fs, env, printf, glob, $a, echo} from "https://deno.land/x/deno_dx/mod.ts";
import {red, yellow, blue, green} from "https://deno.land/std@0.108.0/fmt/colors.ts";

// aliases
$.alias("ll", "ls -al");

// prompt to input your name
let name = await question(blue("what's your name: "));
echo("Hello ", blue(name ?? "guest"));

// pwd(), env variables and params
echo("Current working directory:", pwd());
echo("Your home:", HOME);
echo("Your name:", USER);
echo("Script name:", $0);

// current file count
const output = await $`ls -1 | wc -l`;
echo("Files count: ", parseInt(output));

// output as lines
for await (const fileName of $a`ls -1 *.ts`) {
    echo("TS file: ", fileName);
}

// alias and output as lines
for await (const fileName of $a`ll *.ts`) {
    echo("TS file: ", fileName);
}

// print your internet outbound ip
let json = await fetch('https://httpbin.org/ip').then(resp => resp.json());
echo("Your ip: ", json.origin)

//printf
printf("hello %s\n", "world");

//glob *.ts
for await (const fileName of glob("*.ts")) {
    echo(`${pwd()}/${fileName}`);
}

Then run dx demo.ts or chmod u+x demo.ts ; ./demo.ts

Task runner support: Taskfile.ts or Taskfile.js

Taskfile.ts or Taskfile.js is file to manage tasks, and you can use dx to run the task.

The task is normal TypeScript’s function with export directive, example as following:

/// <reference lib="esnext" />
import {$, cd, pwd, question, os, fs, env, printf, glob, $a, echo} from "https://deno.land/x/deno_dx/mod.ts";
import {red, yellow, blue, green} from "https://deno.land/std@0.99.0/fmt/colors.ts";

export default hello;

export async function hello() {
    echo(green("Hello"));
}

hello.desc = "Hello task";

export async function first() {
    console.log(blue("first task"));
}

Then execute dx hello to run task.

  • dx --tasks to list tasks in Taskfile.ts or Taskfile.js
  • Task names completion with o-my-zsh. Please add dx to plugins in ~/.zshrc.
mkdir -p ~/.oh-my-zsh/custom/plugins/dx/
dx -c zsh > ~/.oh-my-zsh/custom/plugins/dx/_dx

functions and variables

import {$, nothrow, cd, pwd, question, os, fs, env} from "https://deno.land/x/deno_dx/mod.ts";
  • $: execute command and return the stdout
  • nothrow: changes behavior of $ to not throw an exception on non-zero exit codes
if( (await nothrow($`lll -al`)).exitCode > 0 ) { 
   console.log("Failed")
}
  • built-in functions: cd, pw, echo, printf, cp, mv, rm, mkdir, getops
  • test: file conditions and single file test only, such as if(test('-e mod.ts')) { }
  • $.alias: introduce alias for command. $.alias("ll", "ls -al")
  • $.export: export env variable for command. $.expoort('NO_COLOR','true');
  • cat: read text file as string
  • read/question: read value from stdin with prompt
  • sleep: await sleep(5);
  • os: OS related functions
  • fs: file system related functions
  • glob: glob files, like commands ls -1 *.ts
for await (const fileName of glob("*.ts")) {
    console.log(fileName);
}
  • env: env global object env.get("HOME")
  • Shell params support: $0(script name), $1 to $9, $[‘@’] for all arguments, $[‘#’] for number of arguments, $[‘*’] for params as a string
  • Shell env variables as global variables in TypeScript automatically.
// builtin env variables for hint, such as USER, HOME, PATH
const output = await $`ls -al  ${HOME}`;
console.log(HOME);

// custom env variables for hint
declare global {
    const JAVA_HOME: string;
}

Execute command

dx supplies three styles to run command, and they are $, $o and $a.

$: execute command with stdout capture

Use $ tag to execute command and return value captured stdout.

let count = parseInt(await $`ls -1 | wc -l`)
console.log(`Files count: ${count}`)

$o: execute command with stdout and stderr

Use $o tag to execute command with stdout and stderr output.

await $o`ls -al`

$a: execute command and convert output into async iterable lines

Use $a tag to execute command and capture stdout and convert output into async iterable lines, then use for await...of to iterate the lines.

for await (const fileName of $a`ls -1 *.ts`) {
    console.log("file: ", fileName);
}

command error handler

If exit code is not 0, and exception will be thrown.

try {
    await $`exit 1`
} catch (p) {
    console.log(`Exit code: ${p.exitCode}`)
    console.log(`Error: ${p.stderr}`)
}

color output

Deno std has fmt/colors.ts already, and you don’t need chalk for simple cases.

import {red, yellow, blue, green} from "https://deno.land/std@0.99.0/fmt/colors.ts";

console.log(green("Hello"));

$ configuration

  • $.shell: set shell for the command and default is which bash
  • $.prefix: prefix for every command line, default is set -euo pipefail; for strict mode.

packages

Examples

grep

import {grep} from "https://deno.land/x/deno_dx/mod.ts";

for await (const line of grep`\\d+`("demo.txt")) {
    console.log("line: ", line);
}

awk

import {awk} from "https://deno.land/x/deno_dx/mod.ts";

for await (const line of awk`{ print }`("demo.txt")) {
    console.log("line: ", line);
}

Misc

dx JetBrains IDEs plugin

References