Skip to content

Commit

Permalink
feat: add .pipe(...) for piping stdout of a command to another comm…
Browse files Browse the repository at this point in the history
…and (#218)
  • Loading branch information
dsherret authored Jan 26, 2024
1 parent 6205ba9 commit 3fefbdc
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 35 deletions.
14 changes: 13 additions & 1 deletion mod.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -797,7 +797,7 @@ Deno.test("piping to stdin", async () => {
assertEquals(result, "1\n2");
}

// command that exists via stdin
// command that exits via stdin
{
const child = $`echo 1 && echo 2 && exit 1`;
const result = await $`deno eval 'await Deno.stdin.readable.pipeTo(Deno.stdout.writable);'`
Expand All @@ -809,6 +809,18 @@ Deno.test("piping to stdin", async () => {
}
});

Deno.test("pipe", async () => {
{
const result = await $`echo 1 && echo 2`
.pipe($`deno eval 'await Deno.stdin.readable.pipeTo(Deno.stderr.writable);'`)
.stderr("piped")
.stdout("piped")
.spawn();
assertEquals(result.stdout, "");
assertEquals(result.stderr, "1\n2\n");
}
});

Deno.test("piping to a writable and the command fails", async () => {
const chunks = [];
let wasClosed = false;
Expand Down
37 changes: 3 additions & 34 deletions mod.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CommandBuilder, CommandResult, escapeArg, getRegisteredCommandNamesSymbol } from "./src/command.ts";
import { CommandBuilder, escapeArg, getRegisteredCommandNamesSymbol, template, templateRaw } from "./src/command.ts";
import {
Box,
Delay,
Expand Down Expand Up @@ -610,16 +610,7 @@ function build$FromState<TExtras extends ExtrasObject = {}>(state: $State<TExtra
};
const result = Object.assign(
(strings: TemplateStringsArray, ...exprs: any[]) => {
let result = "";
for (let i = 0; i < Math.max(strings.length, exprs.length); i++) {
if (strings.length > i) {
result += strings[i];
}
if (exprs.length > i) {
result += templateLiteralExprToString(exprs[i], escapeArg);
}
}
return state.commandBuilder.getValue().command(result);
return state.commandBuilder.getValue().command(template(strings, exprs));
},
helperObject,
logDepthObj,
Expand Down Expand Up @@ -747,16 +738,7 @@ function build$FromState<TExtras extends ExtrasObject = {}>(state: $State<TExtra
return state.requestBuilder.url(url);
},
raw(strings: TemplateStringsArray, ...exprs: any[]) {
let result = "";
for (let i = 0; i < Math.max(strings.length, exprs.length); i++) {
if (strings.length > i) {
result += strings[i];
}
if (exprs.length > i) {
result += templateLiteralExprToString(exprs[i]);
}
}
return state.commandBuilder.getValue().command(result);
return state.commandBuilder.getValue().command(templateRaw(strings, exprs));
},
withRetries<TReturn>(opts: RetryOptions<TReturn>): Promise<TReturn> {
return withRetries(result, state.errorLogger.getValue(), opts);
Expand Down Expand Up @@ -864,19 +846,6 @@ export function build$<TExtras extends ExtrasObject = {}>(
}));
}

function templateLiteralExprToString(expr: any, escape?: (arg: string) => string): string {
let result: string;
if (expr instanceof Array) {
return expr.map((e) => templateLiteralExprToString(e, escape)).join(" ");
} else if (expr instanceof CommandResult) {
// remove last newline
result = expr.stdout.replace(/\r?\n$/, "");
} else {
result = `${expr}`;
}
return escape ? escape(result) : result;
}

/**
* Default `$` instance where commands may be executed.
*/
Expand Down
55 changes: 55 additions & 0 deletions src/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,22 @@ export class CommandBuilder implements PromiseLike<CommandResult> {
});
}

/** Pipes the current command to the provided command returning the
* provided command builder. When chaining, it's important to call this
* after you are done configuring the current command or else you will
* start modifying the provided command instead.
*
* @example
* ```ts
* const lineCount = await $`echo 1 && echo 2`
* .pipe($`wc -l`)
* .text();
* ```
*/
pipe(builder: CommandBuilder): CommandBuilder {
return builder.stdin(this.stdout("piped"));
}

/** Sets multiple environment variables to use at the same time via an object literal. */
env(items: Record<string, string | undefined>): CommandBuilder;
/** Sets a single environment variable to use. */
Expand Down Expand Up @@ -1166,3 +1182,42 @@ function signalCausesAbort(signal: Deno.Signal) {
return false;
}
}

export function template(strings: TemplateStringsArray, exprs: any[]) {
let result = "";
for (let i = 0; i < Math.max(strings.length, exprs.length); i++) {
if (strings.length > i) {
result += strings[i];
}
if (exprs.length > i) {
result += templateLiteralExprToString(exprs[i], escapeArg);
}
}
return result;
}

export function templateRaw(strings: TemplateStringsArray, exprs: any[]) {
let result = "";
for (let i = 0; i < Math.max(strings.length, exprs.length); i++) {
if (strings.length > i) {
result += strings[i];
}
if (exprs.length > i) {
result += templateLiteralExprToString(exprs[i]);
}
}
return result;
}

function templateLiteralExprToString(expr: any, escape?: (arg: string) => string): string {
let result: string;
if (expr instanceof Array) {
return expr.map((e) => templateLiteralExprToString(e, escape)).join(" ");
} else if (expr instanceof CommandResult) {
// remove last newline
result = expr.stdout.replace(/\r?\n$/, "");
} else {
result = `${expr}`;
}
return escape ? escape(result) : result;
}

0 comments on commit 3fefbdc

Please sign in to comment.