Skip to content

Commit

Permalink
docs: update for 0.38 (#233)
Browse files Browse the repository at this point in the history
  • Loading branch information
dsherret committed Jan 29, 2024
1 parent 634af83 commit ea75093
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 12 deletions.
91 changes: 86 additions & 5 deletions README.md
Expand Up @@ -12,6 +12,7 @@ Cross platform shell tools for Deno inspired by [zx](https://github.com/google/z
- Makes more code work on Windows.
- Allows exporting the shell's environment to the current process.
- Uses [deno_task_shell](https://github.com/denoland/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.
Expand All @@ -27,8 +28,8 @@ import $ from "https://deno.land/x/dax/mod.ts";
// run a command
await $`echo 5`; // outputs: 5

// more complex example outputting 1 to stdout and 2 to stderr
await $`echo 1 && deno eval 'console.error(2);'`;
// outputting 1 to stdout and running a sub process
await $`echo 1 && deno run main.ts`;

// parallel
await Promise.all([
Expand Down Expand Up @@ -57,8 +58,8 @@ console.log(result.prop); // 5
Get the result of stdout as bytes (makes stdout "quiet"):

```ts
const result = await $`echo 'test'`.bytes();
console.log(result); // Uint8Array(5) [ 116, 101, 115, 116, 10 ]
const bytes = await $`gzip < file.txt`.bytes();
console.log(bytes);
```

Get the result of stdout as a list of lines (makes stdout "quiet"):
Expand Down Expand Up @@ -106,12 +107,31 @@ Piping to a `WritableStream`:
await $`echo 1`.stdout(Deno.stderr.writable, { preventClose: true });
```

Or to a file:
To a file path:

```ts
await $`echo 1`.stdout($.path("data.txt"));
```

To a file:

```ts
using file = $.path("data.txt").openSync({ write: true, create: true });
await $`echo 1`.stdout(file);
```

From one command to another:

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

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

### Providing arguments to a command

Use an expression in a template literal to provide a single argument to a command:
Expand Down Expand Up @@ -159,6 +179,37 @@ const finalText = await $`echo ${result}`.text();
console.log(finalText); // 1
```

#### JavaScript objects to redirects

You can also provide JavaScript objects to shell output redirects:

```ts
const buffer = new Uint8Array(2);
await $`echo 1 && (echo 2 > ${buffer}) && echo 3`; // 1\n3\n
console.log(buffer); // Uint8Array(2) [ 50, 10 ] (2\n)
```

Supported objects: `Uint8Array`, `PathRef`, `WritableStream`, function that returns a `WritableStream`, any object that implements `[$.symbols.writable](): WritableStream`

Or input redirects:

```ts
// strings
const data = "my data in a string";
const bytes = await $`gzip < ${data}`;

// paths
const path = $.path("file.txt");
const bytes = await $`gzip < ${path}`;

// requests (this example does not make the request until after 5 seconds)
const request = $.request("https://plugins.dprint.dev/info.json")
.showProgress(); // show a progress bar while downloading
const bytes = await $`sleep 5 && gzip < ${request}`.bytes();
```

Supported objects: `string`, `Uint8Array`, `PathRef`, `RequestBuilder`, `ReadableStream`, function that returns a `ReadableStream`, any object that implements `[$.symbols.readable](): ReadableStream`

### Providing stdin

```ts
Expand Down Expand Up @@ -670,6 +721,17 @@ console.log(response.code);
console.log(await response.json());
```

Requests can be piped to commands:

```ts
const request = $.request("https://plugins.dprint.dev/info.json");
await $`deno run main.ts`.stdin(request);

// or as a redirect... this sleeps 5 seconds, then makes
// request and redirects the output to the command
await $`sleep 5 && deno run main.ts < ${request}`;
```

See the [documentation on `RequestBuilder`](https://deno.land/x/dax/src/request.ts?s=RequestBuilder) for more details. It should be as flexible as `fetch`, but uses a builder API (ex. set headers via `.header(...)`).

### Showing progress
Expand Down Expand Up @@ -703,6 +765,25 @@ await $`echo 1 && echo 2`;
await $`echo 1 || echo 2`;
```

Pipe sequences:

```ts
await $`echo 1 | deno run main.ts`;
```

Redirects:

```ts
await $`echo 1 > output.txt`;
const gzippedBytes = await $`gzip < input.txt`.bytes();
```

Sub shells:

```ts
await $`(echo 1 && echo 2) > output.txt`;
```

Setting env var for command in the shell (generally you can just use `.env(...)` though):

```ts
Expand Down
14 changes: 14 additions & 0 deletions mod.test.ts
Expand Up @@ -1269,6 +1269,12 @@ Deno.test("input redirects with provided object", async () => {
const output = await $`cat - < ${stream}`.text();
assertEquals(output, text);
}
// string
{
const text = "testing".repeat(1000);
const output = await $`cat - < ${text}`.text();
assertEquals(output, text);
}
// bytes
{
const text = "testing".repeat(1000);
Expand Down Expand Up @@ -1361,6 +1367,14 @@ Deno.test("output redirect with provided object", async () => {
await $`echo 1 > ${() => writableStream}`;
assertEquals(chunks, [new Uint8Array([49, 10])]);
}
{
assertThrows(
() => $`echo 1 > ${"test.txt"}`,
Error,
"Failed resolving expression in command. Cannot provide strings to output " +
"redirects. Did you mean to provide a path instead via the `$.path(...)` API?",
);
}
});

Deno.test("shebang support", async (t) => {
Expand Down
11 changes: 6 additions & 5 deletions mod.ts
Expand Up @@ -79,14 +79,15 @@ export type {
*
* Differences:
*
* 1. Minimal globals or global configuration.
* - Only a default instance of `$`, but it's not mandatory to use this.
* 1. No custom CLI.
* 1. Cross platform shell.
* - Makes more code work on Windows.
* - Uses [deno_task_shell](https://github.com/denoland/deno_task_shell)'s parser.
* - Allows exporting the shell's environment to the current process.
* 1. Good for application code in addition to use as a shell script replacement
* - Uses [deno_task_shell](https://github.com/denoland/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.
*
* ## Example
Expand Down
17 changes: 15 additions & 2 deletions src/command.ts
Expand Up @@ -1252,8 +1252,17 @@ function templateInner(
const expr = exprs[i];
const inputOrOutputRedirect = detectInputOrOutputRedirect(text);
if (inputOrOutputRedirect === "<") {
if (typeof expr === "string" || expr instanceof PathRef) {
if (expr instanceof PathRef) {
text += templateLiteralExprToString(expr, escape);
} else if (typeof expr === "string") {
handleReadableStream(() =>
new ReadableStream({
start(controller) {
controller.enqueue(new TextEncoder().encode(expr));
controller.close();
},
})
);
} else if (expr instanceof ReadableStream) {
handleReadableStream(() => expr);
} else if (expr?.[symbols.readable]) {
Expand Down Expand Up @@ -1303,7 +1312,7 @@ function templateInner(
throw new Error("Unsupported object provided to input redirect.");
}
} else if (inputOrOutputRedirect === ">") {
if (typeof expr === "string" || expr instanceof PathRef) {
if (expr instanceof PathRef) {
text += templateLiteralExprToString(expr, escape);
} else if (expr instanceof WritableStream) {
handleWritableStream(() => expr);
Expand Down Expand Up @@ -1349,6 +1358,10 @@ function templateInner(
);
}
});
} else if (typeof expr === "string") {
throw new Error(
"Cannot provide strings to output redirects. Did you mean to provide a path instead via the `$.path(...)` API?",
);
} else {
throw new Error("Unsupported object provided to output redirect.");
}
Expand Down

0 comments on commit ea75093

Please sign in to comment.