Skip to content

Commit

Permalink
Start on this, but not going to use it.
Browse files Browse the repository at this point in the history
  • Loading branch information
dsherret committed Feb 4, 2024
1 parent 27ab9cb commit 4c4d148
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 20 deletions.
22 changes: 21 additions & 1 deletion mod.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2108,12 +2108,32 @@ Deno.test("should support empty quoted string", async () => {
assertEquals(output, " test ");
});

Deno.test("esnure deprecated PathRef export still works", () => {
Deno.test("ensure deprecated PathRef export still works", () => {
const path = new PathRef("hello");
assert(path instanceof Path);
assert(path instanceof PathRef);
});

Deno.test("providing command builder to another command as args", async () => {
{
// these will be executed in parallel
const cmd1 = $`echo 1`;
const cmd2 = $`echo 2`;
const cmd3 = $`echo 3`;
const output = await $`echo ${cmd1} ${cmd2} ${cmd3}`.text();
assertEquals(output, "1 2");
}
{
const cmd1 = $`echo 1`;
const cmd2 = $`exit 10`;
// it will still throw because the arguments are evaluated
const output = await $`echo ${cmd1} ${cmd2}`.noThrow();
assertEquals(output.stderr, "");
assertEquals(output.stdout, "");
assertEquals(output.code, 10);
}
});

function ensurePromiseNotResolved(promise: Promise<unknown>) {
return new Promise<void>((resolve, reject) => {
promise.then(() => reject(new Error("Promise was resolved")));
Expand Down
10 changes: 5 additions & 5 deletions mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
CommandBuilder,
escapeArg,
getRegisteredCommandNamesSymbol,
setCommandTextAndFdsSymbol,
setCommandTextStateSymbol,
template,
templateRaw,
} from "./src/command.ts";
Expand Down Expand Up @@ -632,8 +632,8 @@ function build$FromState<TExtras extends ExtrasObject = {}>(state: $State<TExtra
};
const result = Object.assign(
(strings: TemplateStringsArray, ...exprs: any[]) => {
const { text, streams } = template(strings, exprs);
return state.commandBuilder.getValue()[setCommandTextAndFdsSymbol](text, streams);
const textState = template(strings, exprs);
return state.commandBuilder.getValue()[setCommandTextStateSymbol](textState);
},
helperObject,
logDepthObj,
Expand Down Expand Up @@ -764,8 +764,8 @@ function build$FromState<TExtras extends ExtrasObject = {}>(state: $State<TExtra
return state.requestBuilder.url(url);
},
raw(strings: TemplateStringsArray, ...exprs: any[]) {
const { text, streams } = templateRaw(strings, exprs);
return state.commandBuilder.getValue()[setCommandTextAndFdsSymbol](text, streams);
const textState = templateRaw(strings, exprs);
return state.commandBuilder.getValue()[setCommandTextStateSymbol](textState);
},
withRetries<TReturn>(opts: RetryOptions<TReturn>): Promise<TReturn> {
return withRetries(result, state.errorLogger.getValue(), opts);
Expand Down
75 changes: 61 additions & 14 deletions src/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,14 @@ interface ShellPipeWriterKindWithOptions {
options?: PipeOptions;
}

/** Command that should be executed before running the main command. */
interface PreCommand {
builder: CommandBuilder;
escape: ((arg: string) => string) | undefined;
}

interface CommandBuilderStateCommand {
text: string;
textItems: [string, ...Array<PreCommand | string>];
fds: StreamFds | undefined;
}

Expand Down Expand Up @@ -103,7 +109,7 @@ const builtInCommands = {
/** @internal */
export const getRegisteredCommandNamesSymbol: unique symbol = Symbol();
/** @internal */
export const setCommandTextAndFdsSymbol: unique symbol = Symbol();
export const setCommandTextStateSymbol: unique symbol = Symbol();

/**
* Underlying builder API for executing commands.
Expand Down Expand Up @@ -241,7 +247,7 @@ export class CommandBuilder implements PromiseLike<CommandResult> {
command = command.map(escapeArg).join(" ");
}
state.command = {
text: command,
textItems: [command],
fds: undefined,
};
});
Expand Down Expand Up @@ -583,12 +589,9 @@ export class CommandBuilder implements PromiseLike<CommandResult> {
}

/** @internal */
[setCommandTextAndFdsSymbol](text: string, fds: StreamFds | undefined) {
[setCommandTextStateSymbol](textState: CommandBuilderStateCommand) {
return this.#newWithState((state) => {
state.command = {
text,
fds,
};
state.command = textState;
});
}
}
Expand Down Expand Up @@ -682,8 +685,12 @@ export function parseAndSpawnCommand(state: CommandBuilderState) {
throw new Error("A command must be set before it can be spawned.");
}

if (state.printCommand) {
state.printCommandLogger.getValue()(state.command.text);
if (
state.printCommand &&
// only print if all text items are resolved
state.command.textItems.length === 1
) {
state.printCommandLogger.getValue()(state.command.textItems[0]);
}

const disposables: Disposable[] = [];
Expand Down Expand Up @@ -723,11 +730,21 @@ export function parseAndSpawnCommand(state: CommandBuilderState) {
state.stderr.kind,
stderrBuffer === "null" ? new NullPipeWriter() : stderrBuffer === "inherit" ? Deno.stderr : stderrBuffer,
);
const { text: commandText, fds } = state.command;
const { fds, textItems } = state.command;
const signal = killSignalController.signal;

return new CommandChild(async (resolve, reject) => {
try {
let commandText: string;
if (textItems.length > 1) {
commandText = await resolveCommandTextItems(textItems);
if (state.printCommand) {
// we have the command text now, so print it out
state.printCommandLogger.getValue()(commandText);
}
} else {
commandText = textItems[0];
}
const list = parseCommand(commandText);
const stdin = await takeStdin();
let code = await spawn(list, {
Expand Down Expand Up @@ -961,6 +978,19 @@ export function parseAndSpawnCommand(state: CommandBuilderState) {
}
}

async function resolveCommandTextItems(commandTextItems: [string, ...Array<PreCommand | string>]) {
const results = await Promise.all(commandTextItems.map((c) => {
if (typeof c === "string") {
return Promise.resolve(c);
} else {
return c.builder.text().then((text) => {
return c.escape?.(text) ?? text;
});
}
}));
return results.join("");
}

/** Result of running a command. */
export class CommandResult {
#stdout: BufferStdio;
Expand Down Expand Up @@ -1240,10 +1270,11 @@ function templateInner(
strings: TemplateStringsArray,
exprs: any[],
escape: ((arg: string) => string) | undefined,
) {
): CommandBuilderStateCommand {
let nextStreamFd = 3;
let text = "";
let streams: StreamFds | undefined;
let textItems: [string, ...Array<string | PreCommand>] | undefined;
const exprsCount = exprs.length;
for (let i = 0; i < Math.max(strings.length, exprs.length); i++) {
if (strings.length > i) {
Expand Down Expand Up @@ -1367,6 +1398,17 @@ function templateInner(
} else {
throw new Error("Unsupported object provided to output redirect.");
}
} else if (expr instanceof CommandBuilder) {
if (textItems == null) {
textItems = [text];
} else {
textItems.push(text);
}
text = "";
textItems.push({
builder: expr,
escape,
});
} else {
text += templateLiteralExprToString(expr, escape);
}
Expand All @@ -1378,9 +1420,14 @@ function templateInner(
}
}
}
if (textItems == null) {
textItems = [text];
} else {
textItems.push(text);
}
return {
text,
streams,
textItems,
fds: streams,
};

function handleReadableStream(createStream: () => ReadableStream) {
Expand Down

0 comments on commit 4c4d148

Please sign in to comment.