Socket
Socket
Sign inDemoInstall

zx

Package Overview
Dependencies
4
Maintainers
2
Versions
111
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 7.2.2 to 7.2.3-dev.0844b88

build/vendor.d.ts

410

build/cli.js
#!/usr/bin/env node
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import fs from 'fs-extra';
import minimist from 'minimist';
import { createRequire } from 'node:module';
import { basename, dirname, extname, join, resolve } from 'node:path';
import url from 'node:url';
import { updateArgv } from './goods.js';
import { $, chalk, fetch, ProcessOutput } from './index.js';
import { startRepl } from './repl.js';
import { randomId } from './util.js';
import { installDeps, parseDeps } from './deps.js';
// src/cli.ts
import { createRequire } from "node:module";
import { basename, dirname, extname, join, resolve } from "node:path";
import url from "node:url";
import {
$,
ProcessOutput,
updateArgv,
fetch,
chalk,
minimist,
fs
} from "./index.js";
import { randomId } from "./util.js";
import { installDeps, parseDeps } from "./deps.js";
function printUsage() {
// language=txt
console.log(`
${chalk.bold('zx ' + getVersion())}
console.log(`
${chalk.bold("zx " + getVersion())}
A tool for writing better scripts
${chalk.bold('Usage')}
${chalk.bold("Usage")}
zx [options] <script>
${chalk.bold('Options')}
${chalk.bold("Options")}
--quiet don't echo commands
--shell=<path> custom shell binary
--prefix=<command> prefix all commands
--postfix=<command> postfix all commands
--eval=<js>, -e evaluate script
--install, -i install dependencies
--experimental enable experimental features
--version, -v print current zx version

@@ -46,203 +38,197 @@ --help, -h print help

}
const argv = minimist(process.argv.slice(2), {
string: ['shell', 'prefix', 'eval'],
boolean: ['version', 'help', 'quiet', 'install', 'repl', 'experimental'],
alias: { e: 'eval', i: 'install', v: 'version', h: 'help' },
stopEarly: true,
var argv = minimist(process.argv.slice(2), {
string: ["shell", "prefix", "postfix", "eval"],
boolean: ["version", "help", "quiet", "verbose", "install", "repl"],
alias: { e: "eval", i: "install", v: "version", h: "help" },
stopEarly: true
});
await (async function main() {
const globals = './globals.js';
await import(globals);
if (argv.quiet)
$.verbose = false;
if (argv.shell)
$.shell = argv.shell;
if (argv.prefix)
$.prefix = argv.prefix;
if (argv.experimental) {
Object.assign(global, await import('./experimental.js'));
}
if (argv.version) {
console.log(getVersion());
return;
}
if (argv.help) {
printUsage();
return;
}
if (argv.repl) {
startRepl();
return;
}
if (argv.eval) {
await runScript(argv.eval);
return;
}
const firstArg = argv._[0];
updateArgv(argv._.slice(firstArg === undefined ? 0 : 1));
if (!firstArg || firstArg === '-') {
const success = await scriptFromStdin();
if (!success)
printUsage();
return;
}
if (/^https?:/.test(firstArg)) {
await scriptFromHttp(firstArg);
return;
}
const filepath = firstArg.startsWith('file:///')
? url.fileURLToPath(firstArg)
: resolve(firstArg);
await importPath(filepath);
})().catch((err) => {
if (err instanceof ProcessOutput) {
console.error('Error:', err.message);
}
else {
console.error(err);
}
process.exitCode = 1;
await async function main() {
const globals = "./globals.js";
await import(globals);
if (argv.verbose)
$.verbose = true;
if (argv.quiet)
$.verbose = false;
if (argv.shell)
$.shell = argv.shell;
if (argv.prefix)
$.prefix = argv.prefix;
if (argv.postfix)
$.postfix = argv.postfix;
if (argv.version) {
console.log(getVersion());
return;
}
if (argv.help) {
printUsage();
return;
}
if (argv.repl) {
await (await import("./repl.js")).startRepl();
return;
}
if (argv.eval) {
await runScript(argv.eval);
return;
}
const firstArg = argv._[0];
updateArgv(argv._.slice(firstArg === void 0 ? 0 : 1));
if (!firstArg || firstArg === "-") {
const success = await scriptFromStdin();
if (!success)
printUsage();
return;
}
if (/^https?:/.test(firstArg)) {
await scriptFromHttp(firstArg);
return;
}
const filepath = firstArg.startsWith("file:///") ? url.fileURLToPath(firstArg) : resolve(firstArg);
await importPath(filepath);
}().catch((err) => {
if (err instanceof ProcessOutput) {
console.error("Error:", err.message);
} else {
console.error(err);
}
process.exitCode = 1;
});
async function runScript(script) {
const filepath = join(process.cwd(), `zx-${randomId()}.mjs`);
await writeAndImport(script, filepath);
const filepath = join(process.cwd(), `zx-${randomId()}.mjs`);
await writeAndImport(script, filepath);
}
async function scriptFromStdin() {
let script = '';
if (!process.stdin.isTTY) {
process.stdin.setEncoding('utf8');
for await (const chunk of process.stdin) {
script += chunk;
}
if (script.length > 0) {
await runScript(script);
return true;
}
let script = "";
if (!process.stdin.isTTY) {
process.stdin.setEncoding("utf8");
for await (const chunk of process.stdin) {
script += chunk;
}
return false;
if (script.length > 0) {
await runScript(script);
return true;
}
}
return false;
}
async function scriptFromHttp(remote) {
const res = await fetch(remote);
if (!res.ok) {
console.error(`Error: Can't get ${remote}`);
process.exit(1);
}
const script = await res.text();
const pathname = new URL(remote).pathname;
const name = basename(pathname);
const ext = extname(pathname) || '.mjs';
const filepath = join(process.cwd(), `${name}-${randomId()}${ext}`);
await writeAndImport(script, filepath);
const res = await fetch(remote);
if (!res.ok) {
console.error(`Error: Can't get ${remote}`);
process.exit(1);
}
const script = await res.text();
const pathname = new URL(remote).pathname;
const name = basename(pathname);
const ext = extname(pathname) || ".mjs";
const filepath = join(process.cwd(), `${name}-${randomId()}${ext}`);
await writeAndImport(script, filepath);
}
async function writeAndImport(script, filepath, origin = filepath) {
await fs.writeFile(filepath, script.toString());
try {
await importPath(filepath, origin);
}
finally {
await fs.rm(filepath);
}
await fs.writeFile(filepath, script.toString());
try {
await importPath(filepath, origin);
} finally {
await fs.rm(filepath);
}
}
async function importPath(filepath, origin = filepath) {
const ext = extname(filepath);
if (ext === '') {
const tmpFilename = fs.existsSync(`${filepath}.mjs`)
? `${basename(filepath)}-${randomId()}.mjs`
: `${basename(filepath)}.mjs`;
return writeAndImport(await fs.readFile(filepath), join(dirname(filepath), tmpFilename), origin);
}
if (ext === '.md') {
return writeAndImport(transformMarkdown(await fs.readFile(filepath)), join(dirname(filepath), basename(filepath) + '.mjs'), origin);
}
if (argv.install) {
const deps = parseDeps(await fs.readFile(filepath));
await installDeps(deps, dirname(filepath));
}
const __filename = resolve(origin);
const __dirname = dirname(__filename);
const require = createRequire(origin);
Object.assign(global, { __filename, __dirname, require });
await import(url.pathToFileURL(filepath).toString());
const ext = extname(filepath);
if (ext === "") {
const tmpFilename = fs.existsSync(`${filepath}.mjs`) ? `${basename(filepath)}-${randomId()}.mjs` : `${basename(filepath)}.mjs`;
return writeAndImport(
await fs.readFile(filepath),
join(dirname(filepath), tmpFilename),
origin
);
}
if (ext === ".md") {
return writeAndImport(
transformMarkdown(await fs.readFile(filepath)),
join(dirname(filepath), basename(filepath) + ".mjs"),
origin
);
}
if (argv.install) {
const deps = parseDeps(await fs.readFile(filepath));
await installDeps(deps, dirname(filepath));
}
const __filename = resolve(origin);
const __dirname = dirname(__filename);
const require2 = createRequire(origin);
Object.assign(global, { __filename, __dirname, require: require2 });
await import(url.pathToFileURL(filepath).toString());
}
function transformMarkdown(buf) {
const source = buf.toString();
const output = [];
let state = 'root';
let codeBlockEnd = '';
let prevLineIsEmpty = true;
const jsCodeBlock = /^(```+|~~~+)(js|javascript)$/;
const shCodeBlock = /^(```+|~~~+)(sh|bash)$/;
const otherCodeBlock = /^(```+|~~~+)(.*)$/;
for (let line of source.split('\n')) {
switch (state) {
case 'root':
if (/^( {4}|\t)/.test(line) && prevLineIsEmpty) {
output.push(line);
state = 'tab';
}
else if (jsCodeBlock.test(line)) {
output.push('');
state = 'js';
codeBlockEnd = line.match(jsCodeBlock)[1];
}
else if (shCodeBlock.test(line)) {
output.push('await $`');
state = 'bash';
codeBlockEnd = line.match(shCodeBlock)[1];
}
else if (otherCodeBlock.test(line)) {
output.push('');
state = 'other';
codeBlockEnd = line.match(otherCodeBlock)[1];
}
else {
prevLineIsEmpty = line === '';
output.push('// ' + line);
}
break;
case 'tab':
if (/^( +|\t)/.test(line)) {
output.push(line);
}
else if (line === '') {
output.push('');
}
else {
output.push('// ' + line);
state = 'root';
}
break;
case 'js':
if (line === codeBlockEnd) {
output.push('');
state = 'root';
}
else {
output.push(line);
}
break;
case 'bash':
if (line === codeBlockEnd) {
output.push('`');
state = 'root';
}
else {
output.push(line);
}
break;
case 'other':
if (line === codeBlockEnd) {
output.push('');
state = 'root';
}
else {
output.push('// ' + line);
}
break;
const source = buf.toString();
const output = [];
let state = "root";
let codeBlockEnd = "";
let prevLineIsEmpty = true;
const jsCodeBlock = /^(```+|~~~+)(js|javascript)$/;
const shCodeBlock = /^(```+|~~~+)(sh|bash)$/;
const otherCodeBlock = /^(```+|~~~+)(.*)$/;
for (let line of source.split("\n")) {
switch (state) {
case "root":
if (/^( {4}|\t)/.test(line) && prevLineIsEmpty) {
output.push(line);
state = "tab";
} else if (jsCodeBlock.test(line)) {
output.push("");
state = "js";
codeBlockEnd = line.match(jsCodeBlock)[1];
} else if (shCodeBlock.test(line)) {
output.push("await $`");
state = "bash";
codeBlockEnd = line.match(shCodeBlock)[1];
} else if (otherCodeBlock.test(line)) {
output.push("");
state = "other";
codeBlockEnd = line.match(otherCodeBlock)[1];
} else {
prevLineIsEmpty = line === "";
output.push("// " + line);
}
break;
case "tab":
if (/^( +|\t)/.test(line)) {
output.push(line);
} else if (line === "") {
output.push("");
} else {
output.push("// " + line);
state = "root";
}
break;
case "js":
if (line === codeBlockEnd) {
output.push("");
state = "root";
} else {
output.push(line);
}
break;
case "bash":
if (line === codeBlockEnd) {
output.push("`");
state = "root";
} else {
output.push(line);
}
break;
case "other":
if (line === codeBlockEnd) {
output.push("");
state = "root";
} else {
output.push("// " + line);
}
break;
}
return output.join('\n');
}
return output.join("\n");
}
function getVersion() {
return createRequire(import.meta.url)('../package.json').version;
return createRequire(import.meta.url)("../package.json").version;
}

@@ -1,25 +0,40 @@

/// <reference types="node" resolution-mode="require"/>
/// <reference types="node" resolution-mode="require"/>
/// <reference types="node" resolution-mode="require"/>
/// <reference types="node" resolution-mode="require"/>
/// <reference types="node" resolution-mode="require"/>
import { ChildProcess, spawn, StdioNull, StdioPipe } from 'node:child_process';
import { spawn, spawnSync, StdioNull, StdioPipe } from 'node:child_process';
import { Readable, Writable } from 'node:stream';
import { inspect } from 'node:util';
import { RequestInfo, RequestInit } from 'node-fetch';
import { RequestInfo, RequestInit } from './vendor.js';
import { Duration, noop, quote } from './util.js';
export type Shell = (pieces: TemplateStringsArray, ...args: any[]) => ProcessPromise;
export interface Shell {
(pieces: TemplateStringsArray, ...args: any[]): ProcessPromise;
(opts: Partial<Options>): Shell;
sync: {
(pieces: TemplateStringsArray, ...args: any[]): ProcessOutput;
(opts: Partial<Options>): Shell;
};
}
declare const processCwd: unique symbol;
export type Options = {
declare const syncExec: unique symbol;
export interface Options {
[processCwd]: string;
[syncExec]: boolean;
cwd?: string;
ac?: AbortController;
input?: string | Buffer | Readable | ProcessOutput | ProcessPromise;
verbose: boolean;
sync: boolean;
env: NodeJS.ProcessEnv;
shell: string | boolean;
nothrow: boolean;
prefix: string;
postfix: string;
quote: typeof quote;
quiet: boolean;
spawn: typeof spawn;
spawnSync: typeof spawnSync;
log: typeof log;
};
kill: typeof kill;
}
export declare function syncProcessCwd(flag?: boolean): void;
export declare const defaults: Options;
export declare function usePowerShell(): void;
export declare function useBash(): void;
export declare const $: Shell & Options;

@@ -29,3 +44,2 @@ type Resolve = (out: ProcessOutput) => void;

export declare class ProcessPromise extends Promise<ProcessOutput> {
child?: ChildProcess;
private _command;

@@ -37,9 +51,11 @@ private _from;

private _stdio;
private _nothrow;
private _quiet;
private _nothrow?;
private _quiet?;
private _timeout?;
private _timeoutSignal?;
private _timeoutSignal;
private _resolved;
private _halted;
private _piped;
private _zurk;
private _output;
_prerun: typeof noop;

@@ -49,2 +65,3 @@ _postrun: typeof noop;

run(): ProcessPromise;
get child(): import("child_process").ChildProcess | undefined;
get stdin(): Writable;

@@ -57,2 +74,3 @@ get stdout(): Readable;

pipe(dest: Writable | ProcessPromise): ProcessPromise;
abort(reason?: string): void;
kill(signal?: string): Promise<void>;

@@ -62,5 +80,8 @@ stdio(stdin: IO, stdout?: IO, stderr?: IO): ProcessPromise;

quiet(): ProcessPromise;
isQuiet(): boolean;
isVerbose(): boolean;
timeout(d: Duration, signal?: string): ProcessPromise;
halt(): ProcessPromise;
get isHalted(): boolean;
get output(): ProcessOutput | null;
}

@@ -75,2 +96,3 @@ export declare class ProcessOutput extends Error {

toString(): string;
valueOf(): string;
get stdout(): string;

@@ -80,6 +102,9 @@ get stderr(): string;

get signal(): NodeJS.Signals | null;
static getExitMessage(code: number | null, signal: NodeJS.Signals | null, stderr: string, from: string): string;
static getErrorMessage(err: NodeJS.ErrnoException, from: string): string;
[inspect.custom](): string;
}
export declare function within<R>(callback: () => R): R;
export declare function cd(dir: string): void;
export declare function cd(dir: string | ProcessOutput): void;
export declare function kill(pid: number, signal?: string): Promise<void>;
export type LogEntry = {

@@ -86,0 +111,0 @@ kind: 'cmd';

@@ -1,361 +0,490 @@

// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import assert from 'node:assert';
import { spawn } from 'node:child_process';
import { AsyncLocalStorage, createHook } from 'node:async_hooks';
import { inspect } from 'node:util';
import chalk from 'chalk';
import which from 'which';
import { errnoMessage, exitCodeInfo, formatCmd, noop, parseDuration, psTree, quote, quotePowerShell, } from './util.js';
const processCwd = Symbol('processCwd');
const storage = new AsyncLocalStorage();
const hook = createHook({
init: syncCwd,
before: syncCwd,
promiseResolve: syncCwd,
after: syncCwd,
destroy: syncCwd,
// src/core.ts
import assert from "node:assert";
import { spawn, spawnSync } from "node:child_process";
import { AsyncLocalStorage, createHook } from "node:async_hooks";
import { inspect } from "node:util";
import {
exec,
buildCmd,
chalk,
which,
ps
} from "./vendor.js";
import {
errnoMessage,
exitCodeInfo,
formatCmd,
getCallerLocation,
noop,
normalizeMultilinePieces,
parseDuration,
quote,
quotePowerShell,
noquote
} from "./util.js";
var processCwd = Symbol("processCwd");
var syncExec = Symbol("syncExec");
var storage = new AsyncLocalStorage();
var cwdSyncHook = createHook({
init: syncCwd,
before: syncCwd,
promiseResolve: syncCwd,
after: syncCwd,
destroy: syncCwd
});
hook.enable();
export const defaults = {
[processCwd]: process.cwd(),
verbose: true,
env: process.env,
shell: true,
prefix: '',
quote: () => {
throw new Error('No quote function is defined: https://ï.at/no-quote-func');
},
spawn,
log,
function syncProcessCwd(flag = true) {
if (flag)
cwdSyncHook.enable();
else
cwdSyncHook.disable();
}
var defaults = {
[processCwd]: process.cwd(),
[syncExec]: false,
verbose: false,
env: process.env,
sync: false,
shell: true,
nothrow: false,
quiet: false,
prefix: "",
postfix: "",
quote: noquote,
spawn,
spawnSync,
log,
kill
};
try {
defaults.shell = which.sync('bash');
defaults.prefix = 'set -euo pipefail;';
defaults.quote = quote;
var isWin = process.platform == "win32";
function usePowerShell() {
$.shell = which.sync("powershell.exe");
$.prefix = "";
$.postfix = "; exit $LastExitCode";
$.quote = quotePowerShell;
}
catch (err) {
if (process.platform == 'win32') {
defaults.shell = which.sync('powershell.exe');
defaults.quote = quotePowerShell;
}
function useBash() {
$.shell = which.sync("bash");
$.prefix = "set -euo pipefail;";
$.quote = quote;
}
function checkShell() {
if (!$.shell) {
throw new Error(`shell is not available: setup guide goes here`);
}
}
function getStore() {
return storage.getStore() || defaults;
return storage.getStore() || defaults;
}
export const $ = new Proxy(function (pieces, ...args) {
const from = new Error().stack.split(/^\s*at\s/m)[2].trim();
if (pieces.some((p) => p == undefined)) {
throw new Error(`Malformed command at ${from}`);
var $ = new Proxy(
function(pieces, ...args) {
checkShell();
if (!Array.isArray(pieces)) {
return function(...args2) {
const self = this;
return within(() => {
return Object.assign($, pieces).apply(self, args2);
});
};
}
const from = getCallerLocation();
if (pieces.some((p) => p == void 0)) {
throw new Error(`Malformed command at ${from}`);
}
let resolve, reject;
const promise = new ProcessPromise((...args) => ([resolve, reject] = args));
let cmd = pieces[0], i = 0;
while (i < args.length) {
let s;
if (Array.isArray(args[i])) {
s = args[i].map((x) => $.quote(substitute(x))).join(' ');
}
else {
s = $.quote(substitute(args[i]));
}
cmd += s + pieces[++i];
}
promise._bind(cmd, from, resolve, reject, getStore());
// Postpone run to allow promise configuration.
setImmediate(() => promise.isHalted || promise.run());
return promise;
}, {
const promise = new ProcessPromise((...args2) => [resolve, reject] = args2);
const cmd = buildCmd(
$.quote,
normalizeMultilinePieces(pieces),
args
);
const snapshot = getStore();
const sync = snapshot[syncExec];
const callback = () => promise.isHalted || promise.run();
promise._bind(
cmd,
from,
resolve,
(v) => {
reject(v);
if (sync)
throw v;
},
snapshot
);
sync ? callback() : setImmediate(callback);
return sync ? promise.output : promise;
},
{
set(_, key, value) {
const target = key in Function.prototype ? _ : getStore();
Reflect.set(target, key, value);
return true;
const target = key in Function.prototype ? _ : getStore();
Reflect.set(target, key === "sync" ? syncExec : key, value);
return true;
},
get(_, key) {
const target = key in Function.prototype ? _ : getStore();
return Reflect.get(target, key);
},
});
function substitute(arg) {
if (arg?.stdout) {
return arg.stdout.replace(/\n$/, '');
if (key === "sync")
return $({ sync: true });
const target = key in Function.prototype ? _ : getStore();
return Reflect.get(target, key);
}
return `${arg}`;
}
);
try {
useBash();
} catch (err) {
}
export class ProcessPromise extends Promise {
constructor() {
super(...arguments);
this._command = '';
this._from = '';
this._resolve = noop;
this._reject = noop;
this._snapshot = getStore();
this._stdio = ['inherit', 'pipe', 'pipe'];
this._nothrow = false;
this._quiet = false;
this._resolved = false;
this._halted = false;
this._piped = false;
this._prerun = noop;
this._postrun = noop;
}
_bind(cmd, from, resolve, reject, options) {
this._command = cmd;
this._from = from;
this._resolve = resolve;
this._reject = reject;
this._snapshot = { ...options };
}
run() {
const $ = this._snapshot;
if (this.child)
return this; // The _run() can be called from a few places.
this._prerun(); // In case $1.pipe($2), the $2 returned, and on $2._run() invoke $1._run().
$.log({
kind: 'cmd',
cmd: this._command,
verbose: $.verbose && !this._quiet,
});
this.child = $.spawn($.prefix + this._command, {
cwd: $.cwd ?? $[processCwd],
shell: typeof $.shell === 'string' ? $.shell : true,
stdio: this._stdio,
windowsHide: true,
env: $.env,
});
this.child.on('close', (code, signal) => {
let message = `exit code: ${code}`;
if (code != 0 || signal != null) {
message = `${stderr || '\n'} at ${this._from}`;
message += `\n exit code: ${code}${exitCodeInfo(code) ? ' (' + exitCodeInfo(code) + ')' : ''}`;
if (signal != null) {
message += `\n signal: ${signal}`;
}
var ProcessPromise = class _ProcessPromise extends Promise {
constructor() {
super(...arguments);
this._command = "";
this._from = "";
this._resolve = noop;
this._reject = noop;
this._snapshot = getStore();
this._stdio = ["inherit", "pipe", "pipe"];
this._timeoutSignal = "SIGTERM";
this._resolved = false;
this._halted = false;
this._piped = false;
this._zurk = null;
this._output = null;
this._prerun = noop;
this._postrun = noop;
}
_bind(cmd, from, resolve, reject, options) {
this._command = cmd;
this._from = from;
this._resolve = resolve;
this._reject = reject;
this._snapshot = { ...options };
}
run() {
if (this.child)
return this;
this._prerun();
const $2 = this._snapshot;
const self = this;
const input = $2.input?.stdout ?? $2.input;
if (input)
this.stdio("pipe");
$2.log({
kind: "cmd",
cmd: this._command,
verbose: self.isVerbose()
});
this._zurk = exec({
input,
cmd: $2.prefix + this._command + $2.postfix,
cwd: $2.cwd ?? $2[processCwd],
ac: $2.ac,
shell: typeof $2.shell === "string" ? $2.shell : true,
env: $2.env,
spawn: $2.spawn,
spawnSync: $2.spawnSync,
stdio: this._stdio,
sync: $2[syncExec],
detached: !isWin,
run: (cb) => cb(),
on: {
start: () => {
if (self._timeout) {
const t = setTimeout(
() => self.kill(self._timeoutSignal),
self._timeout
);
self.finally(() => clearTimeout(t)).catch(noop);
}
},
stdout: (data) => {
if (self._piped)
return;
$2.log({ kind: "stdout", data, verbose: self.isVerbose() });
},
stderr: (data) => {
$2.log({ kind: "stderr", data, verbose: !self.isQuiet() });
},
end: ({ error, stdout, stderr, stdall, status, signal }) => {
self._resolved = true;
if (error) {
const message = ProcessOutput.getErrorMessage(error, self._from);
const output = new ProcessOutput(
null,
null,
stdout,
stderr,
stdall,
message
);
self._output = output;
self._reject(output);
} else {
const message = ProcessOutput.getExitMessage(
status,
signal,
stderr,
self._from
);
const output = new ProcessOutput(
status,
signal,
stdout,
stderr,
stdall,
message
);
self._output = output;
if (status === 0 || (self._nothrow ?? $2.nothrow)) {
self._resolve(output);
} else {
self._reject(output);
}
let output = new ProcessOutput(code, signal, stdout, stderr, combined, message);
if (code === 0 || this._nothrow) {
this._resolve(output);
}
else {
this._reject(output);
}
this._resolved = true;
});
this.child.on('error', (err) => {
const message = `${err.message}\n` +
` errno: ${err.errno} (${errnoMessage(err.errno)})\n` +
` code: ${err.code}\n` +
` at ${this._from}`;
this._reject(new ProcessOutput(null, null, stdout, stderr, combined, message));
this._resolved = true;
});
let stdout = '', stderr = '', combined = '';
let onStdout = (data) => {
$.log({ kind: 'stdout', data, verbose: $.verbose && !this._quiet });
stdout += data;
combined += data;
};
let onStderr = (data) => {
$.log({ kind: 'stderr', data, verbose: $.verbose && !this._quiet });
stderr += data;
combined += data;
};
if (!this._piped)
this.child.stdout?.on('data', onStdout); // If process is piped, don't collect or print output.
this.child.stderr?.on('data', onStderr); // Stderr should be printed regardless of piping.
this._postrun(); // In case $1.pipe($2), after both subprocesses are running, we can pipe $1.stdout to $2.stdin.
if (this._timeout && this._timeoutSignal) {
const t = setTimeout(() => this.kill(this._timeoutSignal), this._timeout);
this.finally(() => clearTimeout(t)).catch(noop);
}
}
return this;
}
});
this._postrun();
return this;
}
get child() {
return this._zurk?.child;
}
get stdin() {
this.stdio("pipe");
this.run();
assert(this.child);
if (this.child.stdin == null)
throw new Error("The stdin of subprocess is null.");
return this.child.stdin;
}
get stdout() {
this.run();
assert(this.child);
if (this.child.stdout == null)
throw new Error("The stdout of subprocess is null.");
return this.child.stdout;
}
get stderr() {
this.run();
assert(this.child);
if (this.child.stderr == null)
throw new Error("The stderr of subprocess is null.");
return this.child.stderr;
}
get exitCode() {
return this.then(
(p) => p.exitCode,
(p) => p.exitCode
);
}
then(onfulfilled, onrejected) {
if (this.isHalted && !this.child) {
throw new Error("The process is halted!");
}
get stdin() {
this.stdio('pipe');
this.run();
assert(this.child);
if (this.child.stdin == null)
throw new Error('The stdin of subprocess is null.');
return this.child.stdin;
return super.then(onfulfilled, onrejected);
}
catch(onrejected) {
return super.catch(onrejected);
}
pipe(dest) {
if (typeof dest == "string")
throw new Error("The pipe() method does not take strings. Forgot $?");
if (this._resolved) {
if (dest instanceof _ProcessPromise)
dest.stdin.end();
throw new Error(
"The pipe() method shouldn't be called after promise is already resolved!"
);
}
get stdout() {
this.run();
assert(this.child);
if (this.child.stdout == null)
throw new Error('The stdout of subprocess is null.');
return this.child.stdout;
this._piped = true;
if (dest instanceof _ProcessPromise) {
dest.stdio("pipe");
dest._prerun = this.run.bind(this);
dest._postrun = () => {
if (!dest.child)
throw new Error(
"Access to stdin of pipe destination without creation a subprocess."
);
this.stdout.pipe(dest.stdin);
};
return dest;
} else {
this._postrun = () => this.stdout.pipe(dest);
return this;
}
get stderr() {
this.run();
assert(this.child);
if (this.child.stderr == null)
throw new Error('The stderr of subprocess is null.');
return this.child.stderr;
}
abort(reason) {
if (!this.child)
throw new Error("Trying to abort a process without creating one.");
this._zurk?.ac.abort(reason);
}
async kill(signal = "SIGTERM") {
if (!this.child)
throw new Error("Trying to kill a process without creating one.");
if (!this.child.pid)
throw new Error("The process pid is undefined.");
return $.kill(this.child.pid, signal);
}
stdio(stdin, stdout = "pipe", stderr = "pipe") {
this._stdio = [stdin, stdout, stderr];
return this;
}
nothrow() {
this._nothrow = true;
return this;
}
quiet() {
this._quiet = true;
return this;
}
isQuiet() {
return this._quiet ?? this._snapshot.quiet;
}
isVerbose() {
return this._snapshot.verbose && !this.isQuiet();
}
timeout(d, signal = "SIGTERM") {
this._timeout = parseDuration(d);
this._timeoutSignal = signal;
return this;
}
halt() {
this._halted = true;
return this;
}
get isHalted() {
return this._halted;
}
get output() {
return this._output;
}
};
var ProcessOutput = class extends Error {
constructor(code, signal, stdout, stderr, combined, message) {
super(message);
this._code = code;
this._signal = signal;
this._stdout = stdout;
this._stderr = stderr;
this._combined = combined;
}
toString() {
return this._combined;
}
valueOf() {
return this._combined.trim();
}
get stdout() {
return this._stdout;
}
get stderr() {
return this._stderr;
}
get exitCode() {
return this._code;
}
get signal() {
return this._signal;
}
static getExitMessage(code, signal, stderr, from) {
let message = `exit code: ${code}`;
if (code != 0 || signal != null) {
message = `${stderr || "\n"} at ${from}`;
message += `
exit code: ${code}${exitCodeInfo(code) ? " (" + exitCodeInfo(code) + ")" : ""}`;
if (signal != null) {
message += `
signal: ${signal}`;
}
}
get exitCode() {
return this.then((p) => p.exitCode, (p) => p.exitCode);
}
then(onfulfilled, onrejected) {
if (this.isHalted && !this.child) {
throw new Error('The process is halted!');
}
return super.then(onfulfilled, onrejected);
}
catch(onrejected) {
return super.catch(onrejected);
}
pipe(dest) {
if (typeof dest == 'string')
throw new Error('The pipe() method does not take strings. Forgot $?');
if (this._resolved) {
if (dest instanceof ProcessPromise)
dest.stdin.end(); // In case of piped stdin, we may want to close stdin of dest as well.
throw new Error("The pipe() method shouldn't be called after promise is already resolved!");
}
this._piped = true;
if (dest instanceof ProcessPromise) {
dest.stdio('pipe');
dest._prerun = this.run.bind(this);
dest._postrun = () => {
if (!dest.child)
throw new Error('Access to stdin of pipe destination without creation a subprocess.');
this.stdout.pipe(dest.stdin);
};
return dest;
}
else {
this._postrun = () => this.stdout.pipe(dest);
return this;
}
}
async kill(signal = 'SIGTERM') {
if (!this.child)
throw new Error('Trying to kill a process without creating one.');
if (!this.child.pid)
throw new Error('The process pid is undefined.');
let children = await psTree(this.child.pid);
for (const p of children) {
try {
process.kill(+p.PID, signal);
}
catch (e) { }
}
try {
process.kill(this.child.pid, signal);
}
catch (e) { }
}
stdio(stdin, stdout = 'pipe', stderr = 'pipe') {
this._stdio = [stdin, stdout, stderr];
return this;
}
nothrow() {
this._nothrow = true;
return this;
}
quiet() {
this._quiet = true;
return this;
}
timeout(d, signal = 'SIGTERM') {
this._timeout = parseDuration(d);
this._timeoutSignal = signal;
return this;
}
halt() {
this._halted = true;
return this;
}
get isHalted() {
return this._halted;
}
}
export class ProcessOutput extends Error {
constructor(code, signal, stdout, stderr, combined, message) {
super(message);
this._code = code;
this._signal = signal;
this._stdout = stdout;
this._stderr = stderr;
this._combined = combined;
}
toString() {
return this._combined;
}
get stdout() {
return this._stdout;
}
get stderr() {
return this._stderr;
}
get exitCode() {
return this._code;
}
get signal() {
return this._signal;
}
[inspect.custom]() {
let stringify = (s, c) => s.length === 0 ? "''" : c(inspect(s));
return `ProcessOutput {
return message;
}
static getErrorMessage(err, from) {
return `${err.message}
errno: ${err.errno} (${errnoMessage(err.errno)})
code: ${err.code}
at ${from}`;
}
[inspect.custom]() {
let stringify = (s, c) => s.length === 0 ? "''" : c(inspect(s));
return `ProcessOutput {
stdout: ${stringify(this.stdout, chalk.green)},
stderr: ${stringify(this.stderr, chalk.red)},
signal: ${inspect(this.signal)},
exitCode: ${(this.exitCode === 0 ? chalk.green : chalk.red)(this.exitCode)}${exitCodeInfo(this.exitCode)
? chalk.grey(' (' + exitCodeInfo(this.exitCode) + ')')
: ''}
exitCode: ${(this.exitCode === 0 ? chalk.green : chalk.red)(this.exitCode)}${exitCodeInfo(this.exitCode) ? chalk.grey(" (" + exitCodeInfo(this.exitCode) + ")") : ""}
}`;
}
}
};
function within(callback) {
return storage.run({ ...getStore() }, callback);
}
export function within(callback) {
return storage.run({ ...getStore() }, callback);
}
function syncCwd() {
if ($[processCwd] != process.cwd())
process.chdir($[processCwd]);
if ($[processCwd] != process.cwd())
process.chdir($[processCwd]);
}
export function cd(dir) {
$.log({ kind: 'cd', dir });
process.chdir(dir);
$[processCwd] = process.cwd();
function cd(dir) {
if (dir instanceof ProcessOutput) {
dir = dir.toString().trim();
}
$.log({ kind: "cd", dir });
process.chdir(dir);
$[processCwd] = process.cwd();
}
export function log(entry) {
switch (entry.kind) {
case 'cmd':
if (!entry.verbose)
return;
process.stderr.write(formatCmd(entry.cmd));
break;
case 'stdout':
case 'stderr':
if (!entry.verbose)
return;
process.stderr.write(entry.data);
break;
case 'cd':
if (!$.verbose)
return;
process.stderr.write('$ ' + chalk.greenBright('cd') + ` ${entry.dir}\n`);
break;
case 'fetch':
if (!$.verbose)
return;
const init = entry.init ? ' ' + inspect(entry.init) : '';
process.stderr.write('$ ' + chalk.greenBright('fetch') + ` ${entry.url}${init}\n`);
break;
case 'retry':
if (!$.verbose)
return;
process.stderr.write(entry.error + '\n');
async function kill(pid, signal) {
let children = await ps.tree({ pid, recursive: true });
for (const p of children) {
try {
process.kill(+p.pid, signal);
} catch (e) {
}
}
try {
process.kill(-pid, signal);
} catch (e) {
}
}
function log(entry) {
switch (entry.kind) {
case "cmd":
if (!entry.verbose)
return;
process.stderr.write(formatCmd(entry.cmd));
break;
case "stdout":
case "stderr":
if (!entry.verbose)
return;
process.stderr.write(entry.data);
break;
case "cd":
if (!$.verbose)
return;
process.stderr.write("$ " + chalk.greenBright("cd") + ` ${entry.dir}
`);
break;
case "fetch":
if (!$.verbose)
return;
const init = entry.init ? " " + inspect(entry.init) : "";
process.stderr.write(
"$ " + chalk.greenBright("fetch") + ` ${entry.url}${init}
`
);
break;
case "retry":
if (!$.verbose)
return;
process.stderr.write(entry.error + "\n");
}
}
export {
$,
ProcessOutput,
ProcessPromise,
cd,
defaults,
kill,
log,
syncProcessCwd,
useBash,
usePowerShell,
within
};

@@ -1,3 +0,2 @@

/// <reference types="node" resolution-mode="require"/>
export declare function installDeps(dependencies: Record<string, string>, prefix?: string): Promise<void>;
export declare function parseDeps(content: Buffer): Record<string, string>;

@@ -1,119 +0,103 @@

// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { $ } from './core.js';
import { spinner } from './experimental.js';
export async function installDeps(dependencies, prefix) {
const packages = Object.entries(dependencies).map(([name, version]) => `${name}@${version}`);
const flags = prefix ? `--prefix=${prefix}` : '';
if (packages.length === 0) {
return;
}
await spinner(`npm i ${packages.join(' ')}`, () => $ `npm install --no-save --no-audit --no-fund ${flags} ${packages}`.nothrow());
// src/deps.ts
import { $ } from "./core.js";
import { spinner } from "./goods.js";
import { depseek } from "./vendor.js";
async function installDeps(dependencies, prefix) {
const packages = Object.entries(dependencies).map(
([name, version]) => `${name}@${version}`
);
const flags = prefix ? `--prefix=${prefix}` : "";
if (packages.length === 0) {
return;
}
await spinner(
`npm i ${packages.join(" ")}`,
() => $`npm install --no-save --no-audit --no-fund ${flags} ${packages}`.nothrow()
);
}
const builtins = new Set([
'_http_agent',
'_http_client',
'_http_common',
'_http_incoming',
'_http_outgoing',
'_http_server',
'_stream_duplex',
'_stream_passthrough',
'_stream_readable',
'_stream_transform',
'_stream_wrap',
'_stream_writable',
'_tls_common',
'_tls_wrap',
'assert',
'async_hooks',
'buffer',
'child_process',
'cluster',
'console',
'constants',
'crypto',
'dgram',
'dns',
'domain',
'events',
'fs',
'http',
'http2',
'https',
'inspector',
'module',
'net',
'os',
'path',
'perf_hooks',
'process',
'punycode',
'querystring',
'readline',
'repl',
'stream',
'string_decoder',
'sys',
'timers',
'tls',
'trace_events',
'tty',
'url',
'util',
'v8',
'vm',
'wasi',
'worker_threads',
'zlib',
var builtins = /* @__PURE__ */ new Set([
"_http_agent",
"_http_client",
"_http_common",
"_http_incoming",
"_http_outgoing",
"_http_server",
"_stream_duplex",
"_stream_passthrough",
"_stream_readable",
"_stream_transform",
"_stream_wrap",
"_stream_writable",
"_tls_common",
"_tls_wrap",
"assert",
"async_hooks",
"buffer",
"child_process",
"cluster",
"console",
"constants",
"crypto",
"dgram",
"dns",
"domain",
"events",
"fs",
"http",
"http2",
"https",
"inspector",
"module",
"net",
"os",
"path",
"perf_hooks",
"process",
"punycode",
"querystring",
"readline",
"repl",
"stream",
"string_decoder",
"sys",
"timers",
"tls",
"trace_events",
"tty",
"url",
"util",
"v8",
"vm",
"wasi",
"worker_threads",
"zlib"
]);
const importRe = [
/\bimport\s+['"](?<path>[^'"]+)['"]/,
/\bimport\(['"](?<path>[^'"]+)['"]\)/,
/\brequire\(['"](?<path>[^'"]+)['"]\)/,
/\bfrom\s+['"](?<path>[^'"]+)['"]/,
];
const nameRe = /^(?<name>(@[a-z0-9-]+\/)?[a-z0-9-.]+)\/?.*$/i;
const versionRe = /(\/\/|\/\*)\s*@(?<version>[~^]?([\dvx*]+([-.][\dx*]+)*))/i;
export function parseDeps(content) {
const deps = {};
const lines = content.toString().split('\n');
for (let line of lines) {
const tuple = parseImports(line);
if (tuple) {
deps[tuple.name] = tuple.version;
}
var nameRe = /^(?<name>(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*)\/?.*$/i;
var versionRe = /^@(?<version>[~^]?(v?[\dx*]+([-.][\d*a-z-]+)*))/i;
function parseDeps(content) {
return depseek(content.toString() + "\n", { comments: true }).reduce((m, { type, value }, i, list) => {
if (type === "dep") {
const meta = list[i + 1];
const name = parsePackageName(value);
const version = meta?.type === "comment" && parseVersion(meta?.value.trim()) || "latest";
if (name)
m[name] = version;
}
return deps;
return m;
}, {});
}
function parseImports(line) {
for (let re of importRe) {
const name = parsePackageName(re.exec(line)?.groups?.path);
const version = parseVersion(line);
if (name) {
return { name, version };
}
}
}
function parsePackageName(path) {
if (!path)
return;
const name = nameRe.exec(path)?.groups?.name;
if (name && !builtins.has(name)) {
return name;
}
if (!path)
return;
const name = nameRe.exec(path)?.groups?.name;
if (name && !builtins.has(name)) {
return name;
}
}
function parseVersion(line) {
return versionRe.exec(line)?.groups?.version || 'latest';
return versionRe.exec(line)?.groups?.version || "latest";
}
export {
installDeps,
parseDeps
};

@@ -1,4 +0,1 @@

/// <reference types="node" resolution-mode="require"/>
/// <reference types="node" resolution-mode="require"/>
/// <reference types="which" />
import * as _ from './index.js';

@@ -20,2 +17,4 @@ declare global {

var globby: typeof _.globby;
var kill: typeof _.kill;
var minimist: typeof _.minimist;
var nothrow: typeof _.nothrow;

@@ -29,5 +28,6 @@ var os: typeof _.os;

var retry: typeof _.retry;
var usePowerShell: typeof _.usePowerShell;
var useBash: typeof _.useBash;
var sleep: typeof _.sleep;
var spinner: typeof _.spinner;
var ssh: typeof _.ssh;
var stdin: typeof _.stdin;

@@ -34,0 +34,0 @@ var which: typeof _.which;

@@ -1,15 +0,3 @@

// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import * as _ from './index.js';
// src/globals.ts
import * as _ from "./index.js";
Object.assign(global, _);

@@ -1,18 +0,29 @@

import * as globbyModule from 'globby';
import minimist from 'minimist';
import { RequestInfo, RequestInit } from 'node-fetch';
import { Duration } from './util.js';
export { default as chalk } from 'chalk';
export { default as fs } from 'fs-extra';
export { default as which } from 'which';
export { default as YAML } from 'yaml';
import { type Duration } from './util.js';
import { minimist, RequestInfo, RequestInit } from './vendor.js';
export { default as path } from 'node:path';
export { default as os } from 'node:os';
export { ssh } from 'webpod';
export * as os from 'node:os';
export declare let argv: minimist.ParsedArgs;
export declare function updateArgv(args: string[]): void;
export declare const globby: ((patterns: string | readonly string[], options?: globbyModule.Options) => Promise<string[]>) & typeof globbyModule;
export declare const glob: ((patterns: string | readonly string[], options?: globbyModule.Options) => Promise<string[]>) & typeof globbyModule;
export declare const globby: typeof import("globby").globby & {
globby: typeof import("globby").globby;
globbySync: typeof import("globby").globbySync;
globbyStream: typeof import("globby").globbyStream;
generateGlobTasksSync: typeof import("globby").generateGlobTasksSync;
generateGlobTasks: typeof import("globby").generateGlobTasks;
isGitIgnoredSync: typeof import("globby").isGitIgnoredSync;
isGitIgnored: typeof import("globby").isGitIgnored;
isDynamicPattern: typeof import("globby").isDynamicPattern;
};
export declare const glob: typeof import("globby").globby & {
globby: typeof import("globby").globby;
globbySync: typeof import("globby").globbySync;
globbyStream: typeof import("globby").globbyStream;
generateGlobTasksSync: typeof import("globby").generateGlobTasksSync;
generateGlobTasks: typeof import("globby").generateGlobTasks;
isGitIgnoredSync: typeof import("globby").isGitIgnoredSync;
isGitIgnored: typeof import("globby").isGitIgnored;
isDynamicPattern: typeof import("globby").isDynamicPattern;
};
export declare function sleep(duration: Duration): Promise<unknown>;
export declare function fetch(url: RequestInfo, init?: RequestInit): Promise<import("node-fetch").Response>;
export declare function fetch(url: RequestInfo, init?: RequestInit): Promise<Response>;
export declare function echo(...args: any[]): void;

@@ -19,0 +30,0 @@ export declare function question(query?: string, options?: {

@@ -1,171 +0,163 @@

// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import assert from 'node:assert';
import * as globbyModule from 'globby';
import minimist from 'minimist';
import nodeFetch from 'node-fetch';
import { createInterface } from 'node:readline';
import { $, within, ProcessOutput } from './core.js';
import { isString, parseDuration } from './util.js';
import chalk from 'chalk';
export { default as chalk } from 'chalk';
export { default as fs } from 'fs-extra';
export { default as which } from 'which';
export { default as YAML } from 'yaml';
export { default as path } from 'node:path';
export { default as os } from 'node:os';
export { ssh } from 'webpod';
export let argv = minimist(process.argv.slice(2));
export function updateArgv(args) {
argv = minimist(args);
global.argv = argv;
// src/goods.ts
import assert from "node:assert";
import { createInterface } from "node:readline";
import { $, within, ProcessOutput } from "./core.js";
import { isString, parseDuration } from "./util.js";
import {
chalk,
minimist,
globbyModule,
nodeFetch
} from "./vendor.js";
import { default as default2 } from "node:path";
import * as os from "node:os";
var argv = minimist(process.argv.slice(2));
function updateArgv(args) {
argv = minimist(args);
global.argv = argv;
}
export const globby = Object.assign(function globby(patterns, options) {
return globbyModule.globby(patterns, options);
var globby = Object.assign(function globby2(patterns, options) {
return globbyModule.globby(patterns, options);
}, globbyModule);
export const glob = globby;
export function sleep(duration) {
return new Promise((resolve) => {
setTimeout(resolve, parseDuration(duration));
});
var glob = globby;
function sleep(duration) {
return new Promise((resolve) => {
setTimeout(resolve, parseDuration(duration));
});
}
export async function fetch(url, init) {
$.log({ kind: 'fetch', url, init });
return nodeFetch(url, init);
async function fetch(url, init) {
$.log({ kind: "fetch", url, init });
return nodeFetch(url, init);
}
export function echo(pieces, ...args) {
let msg;
const lastIdx = pieces.length - 1;
if (Array.isArray(pieces) &&
pieces.every(isString) &&
lastIdx === args.length) {
msg =
args.map((a, i) => pieces[i] + stringify(a)).join('') + pieces[lastIdx];
}
else {
msg = [pieces, ...args].map(stringify).join(' ');
}
console.log(msg);
function echo(pieces, ...args) {
let msg;
const lastIdx = pieces.length - 1;
if (Array.isArray(pieces) && pieces.every(isString) && lastIdx === args.length) {
msg = args.map((a, i) => pieces[i] + stringify(a)).join("") + pieces[lastIdx];
} else {
msg = [pieces, ...args].map(stringify).join(" ");
}
console.log(msg);
}
function stringify(arg) {
if (arg instanceof ProcessOutput) {
return arg.toString().replace(/\n$/, '');
}
return `${arg}`;
if (arg instanceof ProcessOutput) {
return arg.toString().replace(/\n$/, "");
}
return `${arg}`;
}
export async function question(query, options) {
let completer = undefined;
if (options && Array.isArray(options.choices)) {
/* c8 ignore next 5 */
completer = function completer(line) {
const completions = options.choices;
const hits = completions.filter((c) => c.startsWith(line));
return [hits.length ? hits : completions, line];
};
}
const rl = createInterface({
input: process.stdin,
output: process.stdout,
terminal: true,
completer,
});
return new Promise((resolve) => rl.question(query ?? '', (answer) => {
rl.close();
resolve(answer);
}));
async function question(query, options) {
let completer = void 0;
if (options && Array.isArray(options.choices)) {
completer = function completer2(line) {
const completions = options.choices;
const hits = completions.filter((c) => c.startsWith(line));
return [hits.length ? hits : completions, line];
};
}
const rl = createInterface({
input: process.stdin,
output: process.stdout,
terminal: true,
completer
});
return new Promise(
(resolve) => rl.question(query ?? "", (answer) => {
rl.close();
resolve(answer);
})
);
}
export async function stdin() {
let buf = '';
process.stdin.setEncoding('utf8');
for await (const chunk of process.stdin) {
buf += chunk;
}
return buf;
async function stdin() {
let buf = "";
process.stdin.setEncoding("utf8");
for await (const chunk of process.stdin) {
buf += chunk;
}
return buf;
}
export async function retry(count, a, b) {
const total = count;
let callback;
let delayStatic = 0;
let delayGen;
if (typeof a == 'function') {
callback = a;
async function retry(count, a, b) {
const total = count;
let callback;
let delayStatic = 0;
let delayGen;
if (typeof a == "function") {
callback = a;
} else {
if (typeof a == "object") {
delayGen = a;
} else {
delayStatic = parseDuration(a);
}
else {
if (typeof a == 'object') {
delayGen = a;
}
else {
delayStatic = parseDuration(a);
}
assert(b);
callback = b;
assert(b);
callback = b;
}
let lastErr;
let attempt = 0;
while (count-- > 0) {
attempt++;
try {
return await callback();
} catch (err) {
let delay = 0;
if (delayStatic > 0)
delay = delayStatic;
if (delayGen)
delay = delayGen.next().value;
$.log({
kind: "retry",
error: chalk.bgRed.white(" FAIL ") + ` Attempt: ${attempt}${total == Infinity ? "" : `/${total}`}` + (delay > 0 ? `; next in ${delay}ms` : "")
});
lastErr = err;
if (count == 0)
break;
if (delay)
await sleep(delay);
}
let lastErr;
let attempt = 0;
while (count-- > 0) {
attempt++;
try {
return await callback();
}
catch (err) {
let delay = 0;
if (delayStatic > 0)
delay = delayStatic;
if (delayGen)
delay = delayGen.next().value;
$.log({
kind: 'retry',
error: chalk.bgRed.white(' FAIL ') +
` Attempt: ${attempt}${total == Infinity ? '' : `/${total}`}` +
(delay > 0 ? `; next in ${delay}ms` : ''),
});
lastErr = err;
if (count == 0)
break;
if (delay)
await sleep(delay);
}
}
throw lastErr;
}
throw lastErr;
}
export function* expBackoff(max = '60s', rand = '100ms') {
const maxMs = parseDuration(max);
const randMs = parseDuration(rand);
let n = 1;
while (true) {
const ms = Math.floor(Math.random() * randMs);
yield Math.min(2 ** n++, maxMs) + ms;
}
function* expBackoff(max = "60s", rand = "100ms") {
const maxMs = parseDuration(max);
const randMs = parseDuration(rand);
let n = 1;
while (true) {
const ms = Math.floor(Math.random() * randMs);
yield Math.min(2 ** n++, maxMs) + ms;
}
}
export async function spinner(title, callback) {
if (typeof title == 'function') {
callback = title;
title = '';
async function spinner(title, callback) {
if (typeof title == "function") {
callback = title;
title = "";
}
let i = 0;
const spin = () => process.stderr.write(` ${"\u280B\u2819\u2839\u2838\u283C\u2834\u2826\u2827\u2807\u280F"[i++ % 10]} ${title}\r`);
return within(async () => {
$.verbose = false;
const id = setInterval(spin, 100);
let result;
try {
result = await callback();
} finally {
clearInterval(id);
process.stderr.write(" ".repeat((process.stdout.columns || 1) - 1) + "\r");
}
let i = 0;
const spin = () => process.stderr.write(` ${'⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'[i++ % 10]} ${title}\r`);
return within(async () => {
$.verbose = false;
const id = setInterval(spin, 100);
let result;
try {
result = await callback();
}
finally {
clearInterval(id);
process.stderr.write(' '.repeat(process.stdout.columns - 1) + '\r');
}
return result;
});
return result;
});
}
export {
argv,
echo,
expBackoff,
fetch,
glob,
globby,
os,
default2 as path,
question,
retry,
sleep,
spinner,
stdin,
updateArgv
};
import { ProcessPromise } from './core.js';
export * from './core.js';
export * from './goods.js';
export { Duration, quote, quotePowerShell } from './util.js';
export { minimist, chalk, fs, which, YAML, ps } from './vendor.js';
export { type Duration, quote, quotePowerShell } from './util.js';
/**
* @deprecated Use $.nothrow() instead.
* @deprecated Use $`cmd`.nothrow() instead.
*/
export declare function nothrow(promise: ProcessPromise): ProcessPromise;
/**
* @deprecated Use $.quiet() instead.
* @deprecated Use $`cmd`.quiet() instead.
*/
export declare function quiet(promise: ProcessPromise): ProcessPromise;

@@ -1,28 +0,23 @@

// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
export * from './core.js';
export * from './goods.js';
export { quote, quotePowerShell } from './util.js';
/**
* @deprecated Use $.nothrow() instead.
*/
export function nothrow(promise) {
return promise.nothrow();
// src/index.ts
export * from "./core.js";
export * from "./goods.js";
import { minimist, chalk, fs, which, YAML, ps } from "./vendor.js";
import { quote, quotePowerShell } from "./util.js";
function nothrow(promise) {
return promise.nothrow();
}
/**
* @deprecated Use $.quiet() instead.
*/
export function quiet(promise) {
return promise.quiet();
function quiet(promise) {
return promise.quiet();
}
export {
YAML,
chalk,
fs,
minimist,
nothrow,
ps,
quiet,
quote,
quotePowerShell,
which
};

@@ -1,1 +0,1 @@

export declare function startRepl(): void;
export declare function startRepl(): Promise<void>;

@@ -1,34 +0,25 @@

// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import chalk from 'chalk';
import os from 'node:os';
import path from 'node:path';
import repl from 'node:repl';
import { inspect } from 'node:util';
import { ProcessOutput, defaults } from './core.js';
export function startRepl() {
defaults.verbose = false;
const r = repl.start({
prompt: chalk.greenBright.bold('❯ '),
useGlobal: true,
preview: false,
writer(output) {
if (output instanceof ProcessOutput) {
return output.toString().replace(/\n$/, '');
}
return inspect(output, { colors: true });
},
});
r.setupHistory(path.join(os.homedir(), '.zx_repl_history'), () => { });
// src/repl.ts
import os from "node:os";
import path from "node:path";
import { inspect } from "node:util";
import { ProcessOutput, defaults } from "./core.js";
import { chalk } from "./vendor.js";
async function startRepl() {
defaults.verbose = false;
const r = (await import("node:repl")).start({
prompt: chalk.greenBright.bold("\u276F "),
useGlobal: true,
preview: false,
writer(output) {
if (output instanceof ProcessOutput) {
return output.toString().replace(/\n$/, "");
}
return inspect(output, { colors: true });
}
});
r.setupHistory(path.join(os.homedir(), ".zx_repl_history"), () => {
});
}
export {
startRepl
};

@@ -1,6 +0,6 @@

import psTreeModule from 'ps-tree';
export declare const psTree: (arg1: number) => Promise<readonly psTreeModule.PS[]>;
export declare function noop(): void;
export declare function randomId(): string;
export declare function isString(obj: any): boolean;
export declare function normalizeMultilinePieces(pieces: TemplateStringsArray): TemplateStringsArray;
export declare function noquote(): string;
export declare function quote(arg: string): string;

@@ -10,4 +10,6 @@ export declare function quotePowerShell(arg: string): string;

export declare function errnoMessage(errno: number | undefined): string;
export type Duration = number | `${number}s` | `${number}ms`;
export type Duration = number | `${number}m` | `${number}s` | `${number}ms`;
export declare function parseDuration(d: Duration): number;
export declare function formatCmd(cmd?: string): string;
export declare function getCallerLocation(err?: Error): string;
export declare function getCallerLocationFromString(stackString?: string): string;

@@ -1,346 +0,355 @@

// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import chalk from 'chalk';
import { promisify } from 'node:util';
import psTreeModule from 'ps-tree';
export const psTree = promisify(psTreeModule);
export function noop() { }
export function randomId() {
return Math.random().toString(36).slice(2);
// src/util.ts
import { chalk, parseLine } from "./vendor.js";
function noop() {
}
export function isString(obj) {
return typeof obj === 'string';
function randomId() {
return Math.random().toString(36).slice(2);
}
export function quote(arg) {
if (/^[a-z0-9/_.\-@:=]+$/i.test(arg) || arg === '') {
return arg;
}
return (`$'` +
arg
.replace(/\\/g, '\\\\')
.replace(/'/g, "\\'")
.replace(/\f/g, '\\f')
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r')
.replace(/\t/g, '\\t')
.replace(/\v/g, '\\v')
.replace(/\0/g, '\\0') +
`'`);
function isString(obj) {
return typeof obj === "string";
}
export function quotePowerShell(arg) {
if (/^[a-z0-9/_.\-]+$/i.test(arg) || arg === '') {
return arg;
}
return `'` + arg.replace(/'/g, "''") + `'`;
var pad = (v) => v === " " ? " " : "";
function normalizeMultilinePieces(pieces) {
return Object.assign(
pieces.map(
(p, i) => p.trim() ? pad(p[0]) + parseLine(p).words.map(({ w }) => w === "\\" ? "" : w.trim()).join(" ") + pad(p[p.length - 1]) : pieces[i]
),
{ raw: pieces.raw }
);
}
export function exitCodeInfo(exitCode) {
return {
2: 'Misuse of shell builtins',
126: 'Invoked command cannot execute',
127: 'Command not found',
128: 'Invalid exit argument',
129: 'Hangup',
130: 'Interrupt',
131: 'Quit and dump core',
132: 'Illegal instruction',
133: 'Trace/breakpoint trap',
134: 'Process aborted',
135: 'Bus error: "access to undefined portion of memory object"',
136: 'Floating point exception: "erroneous arithmetic operation"',
137: 'Kill (terminate immediately)',
138: 'User-defined 1',
139: 'Segmentation violation',
140: 'User-defined 2',
141: 'Write to pipe with no one reading',
142: 'Signal raised by alarm',
143: 'Termination (request to terminate)',
145: 'Child process terminated, stopped (or continued*)',
146: 'Continue if stopped',
147: 'Stop executing temporarily',
148: 'Terminal stop signal',
149: 'Background process attempting to read from tty ("in")',
150: 'Background process attempting to write to tty ("out")',
151: 'Urgent data available on socket',
152: 'CPU time limit exceeded',
153: 'File size limit exceeded',
154: 'Signal raised by timer counting virtual time: "virtual timer expired"',
155: 'Profiling timer expired',
157: 'Pollable event',
159: 'Bad syscall',
}[exitCode || -1];
function noquote() {
throw new Error("No quote function is defined: https://\xEF.at/no-quote-func");
}
export function errnoMessage(errno) {
if (errno === undefined) {
return 'Unknown error';
}
return ({
0: 'Success',
1: 'Not super-user',
2: 'No such file or directory',
3: 'No such process',
4: 'Interrupted system call',
5: 'I/O error',
6: 'No such device or address',
7: 'Arg list too long',
8: 'Exec format error',
9: 'Bad file number',
10: 'No children',
11: 'No more processes',
12: 'Not enough core',
13: 'Permission denied',
14: 'Bad address',
15: 'Block device required',
16: 'Mount device busy',
17: 'File exists',
18: 'Cross-device link',
19: 'No such device',
20: 'Not a directory',
21: 'Is a directory',
22: 'Invalid argument',
23: 'Too many open files in system',
24: 'Too many open files',
25: 'Not a typewriter',
26: 'Text file busy',
27: 'File too large',
28: 'No space left on device',
29: 'Illegal seek',
30: 'Read only file system',
31: 'Too many links',
32: 'Broken pipe',
33: 'Math arg out of domain of func',
34: 'Math result not representable',
35: 'File locking deadlock error',
36: 'File or path name too long',
37: 'No record locks available',
38: 'Function not implemented',
39: 'Directory not empty',
40: 'Too many symbolic links',
42: 'No message of desired type',
43: 'Identifier removed',
44: 'Channel number out of range',
45: 'Level 2 not synchronized',
46: 'Level 3 halted',
47: 'Level 3 reset',
48: 'Link number out of range',
49: 'Protocol driver not attached',
50: 'No CSI structure available',
51: 'Level 2 halted',
52: 'Invalid exchange',
53: 'Invalid request descriptor',
54: 'Exchange full',
55: 'No anode',
56: 'Invalid request code',
57: 'Invalid slot',
59: 'Bad font file fmt',
60: 'Device not a stream',
61: 'No data (for no delay io)',
62: 'Timer expired',
63: 'Out of streams resources',
64: 'Machine is not on the network',
65: 'Package not installed',
66: 'The object is remote',
67: 'The link has been severed',
68: 'Advertise error',
69: 'Srmount error',
70: 'Communication error on send',
71: 'Protocol error',
72: 'Multihop attempted',
73: 'Cross mount point (not really error)',
74: 'Trying to read unreadable message',
75: 'Value too large for defined data type',
76: 'Given log. name not unique',
77: 'f.d. invalid for this operation',
78: 'Remote address changed',
79: 'Can access a needed shared lib',
80: 'Accessing a corrupted shared lib',
81: '.lib section in a.out corrupted',
82: 'Attempting to link in too many libs',
83: 'Attempting to exec a shared library',
84: 'Illegal byte sequence',
86: 'Streams pipe error',
87: 'Too many users',
88: 'Socket operation on non-socket',
89: 'Destination address required',
90: 'Message too long',
91: 'Protocol wrong type for socket',
92: 'Protocol not available',
93: 'Unknown protocol',
94: 'Socket type not supported',
95: 'Not supported',
96: 'Protocol family not supported',
97: 'Address family not supported by protocol family',
98: 'Address already in use',
99: 'Address not available',
100: 'Network interface is not configured',
101: 'Network is unreachable',
102: 'Connection reset by network',
103: 'Connection aborted',
104: 'Connection reset by peer',
105: 'No buffer space available',
106: 'Socket is already connected',
107: 'Socket is not connected',
108: "Can't send after socket shutdown",
109: 'Too many references',
110: 'Connection timed out',
111: 'Connection refused',
112: 'Host is down',
113: 'Host is unreachable',
114: 'Socket already connected',
115: 'Connection already in progress',
116: 'Stale file handle',
122: 'Quota exceeded',
123: 'No medium (in tape drive)',
125: 'Operation canceled',
130: 'Previous owner died',
131: 'State not recoverable',
}[-errno] || 'Unknown error');
function quote(arg) {
if (/^[a-z0-9/_.\-@:=]+$/i.test(arg) || arg === "") {
return arg;
}
return `$'` + arg.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\f/g, "\\f").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t").replace(/\v/g, "\\v").replace(/\0/g, "\\0") + `'`;
}
export function parseDuration(d) {
if (typeof d == 'number') {
if (isNaN(d) || d < 0)
throw new Error(`Invalid duration: "${d}".`);
return d;
}
else if (/\d+s/.test(d)) {
return +d.slice(0, -1) * 1000;
}
else if (/\d+ms/.test(d)) {
return +d.slice(0, -2);
}
throw new Error(`Unknown duration: "${d}".`);
function quotePowerShell(arg) {
if (/^[a-z0-9/_.\-]+$/i.test(arg) || arg === "") {
return arg;
}
return `'` + arg.replace(/'/g, "''") + `'`;
}
export function formatCmd(cmd) {
if (cmd == undefined)
return chalk.grey('undefined');
const chars = [...cmd];
let out = '$ ';
let buf = '';
let ch;
let state = root;
let wordCount = 0;
while (state) {
ch = chars.shift() || 'EOF';
if (ch == '\n') {
out += style(state, buf) + '\n> ';
buf = '';
continue;
}
const next = ch == 'EOF' ? undefined : state();
if (next != state) {
out += style(state, buf);
buf = '';
}
state = next == root ? next() : next;
buf += ch;
function exitCodeInfo(exitCode) {
return {
2: "Misuse of shell builtins",
126: "Invoked command cannot execute",
127: "Command not found",
128: "Invalid exit argument",
129: "Hangup",
130: "Interrupt",
131: "Quit and dump core",
132: "Illegal instruction",
133: "Trace/breakpoint trap",
134: "Process aborted",
135: 'Bus error: "access to undefined portion of memory object"',
136: 'Floating point exception: "erroneous arithmetic operation"',
137: "Kill (terminate immediately)",
138: "User-defined 1",
139: "Segmentation violation",
140: "User-defined 2",
141: "Write to pipe with no one reading",
142: "Signal raised by alarm",
143: "Termination (request to terminate)",
145: "Child process terminated, stopped (or continued*)",
146: "Continue if stopped",
147: "Stop executing temporarily",
148: "Terminal stop signal",
149: 'Background process attempting to read from tty ("in")',
150: 'Background process attempting to write to tty ("out")',
151: "Urgent data available on socket",
152: "CPU time limit exceeded",
153: "File size limit exceeded",
154: 'Signal raised by timer counting virtual time: "virtual timer expired"',
155: "Profiling timer expired",
157: "Pollable event",
159: "Bad syscall"
}[exitCode || -1];
}
function errnoMessage(errno) {
if (errno === void 0) {
return "Unknown error";
}
return {
0: "Success",
1: "Not super-user",
2: "No such file or directory",
3: "No such process",
4: "Interrupted system call",
5: "I/O error",
6: "No such device or address",
7: "Arg list too long",
8: "Exec format error",
9: "Bad file number",
10: "No children",
11: "No more processes",
12: "Not enough core",
13: "Permission denied",
14: "Bad address",
15: "Block device required",
16: "Mount device busy",
17: "File exists",
18: "Cross-device link",
19: "No such device",
20: "Not a directory",
21: "Is a directory",
22: "Invalid argument",
23: "Too many open files in system",
24: "Too many open files",
25: "Not a typewriter",
26: "Text file busy",
27: "File too large",
28: "No space left on device",
29: "Illegal seek",
30: "Read only file system",
31: "Too many links",
32: "Broken pipe",
33: "Math arg out of domain of func",
34: "Math result not representable",
35: "File locking deadlock error",
36: "File or path name too long",
37: "No record locks available",
38: "Function not implemented",
39: "Directory not empty",
40: "Too many symbolic links",
42: "No message of desired type",
43: "Identifier removed",
44: "Channel number out of range",
45: "Level 2 not synchronized",
46: "Level 3 halted",
47: "Level 3 reset",
48: "Link number out of range",
49: "Protocol driver not attached",
50: "No CSI structure available",
51: "Level 2 halted",
52: "Invalid exchange",
53: "Invalid request descriptor",
54: "Exchange full",
55: "No anode",
56: "Invalid request code",
57: "Invalid slot",
59: "Bad font file fmt",
60: "Device not a stream",
61: "No data (for no delay io)",
62: "Timer expired",
63: "Out of streams resources",
64: "Machine is not on the network",
65: "Package not installed",
66: "The object is remote",
67: "The link has been severed",
68: "Advertise error",
69: "Srmount error",
70: "Communication error on send",
71: "Protocol error",
72: "Multihop attempted",
73: "Cross mount point (not really error)",
74: "Trying to read unreadable message",
75: "Value too large for defined data type",
76: "Given log. name not unique",
77: "f.d. invalid for this operation",
78: "Remote address changed",
79: "Can access a needed shared lib",
80: "Accessing a corrupted shared lib",
81: ".lib section in a.out corrupted",
82: "Attempting to link in too many libs",
83: "Attempting to exec a shared library",
84: "Illegal byte sequence",
86: "Streams pipe error",
87: "Too many users",
88: "Socket operation on non-socket",
89: "Destination address required",
90: "Message too long",
91: "Protocol wrong type for socket",
92: "Protocol not available",
93: "Unknown protocol",
94: "Socket type not supported",
95: "Not supported",
96: "Protocol family not supported",
97: "Address family not supported by protocol family",
98: "Address already in use",
99: "Address not available",
100: "Network interface is not configured",
101: "Network is unreachable",
102: "Connection reset by network",
103: "Connection aborted",
104: "Connection reset by peer",
105: "No buffer space available",
106: "Socket is already connected",
107: "Socket is not connected",
108: "Can't send after socket shutdown",
109: "Too many references",
110: "Connection timed out",
111: "Connection refused",
112: "Host is down",
113: "Host is unreachable",
114: "Socket already connected",
115: "Connection already in progress",
116: "Stale file handle",
122: "Quota exceeded",
123: "No medium (in tape drive)",
125: "Operation canceled",
130: "Previous owner died",
131: "State not recoverable"
}[-errno] || "Unknown error";
}
function parseDuration(d) {
if (typeof d == "number") {
if (isNaN(d) || d < 0)
throw new Error(`Invalid duration: "${d}".`);
return d;
} else if (/\d+s/.test(d)) {
return +d.slice(0, -1) * 1e3;
} else if (/\d+ms/.test(d)) {
return +d.slice(0, -2);
} else if (/\d+m/.test(d)) {
return +d.slice(0, -1) * 1e3 * 60;
}
throw new Error(`Unknown duration: "${d}".`);
}
function formatCmd(cmd) {
if (cmd == void 0)
return chalk.grey("undefined");
const chars = [...cmd];
let out = "$ ";
let buf = "";
let ch;
let state = root;
let wordCount = 0;
while (state) {
ch = chars.shift() || "EOF";
if (ch == "\n") {
out += style(state, buf) + "\n> ";
buf = "";
continue;
}
function style(state, s) {
if (s == '')
return '';
if (reservedWords.includes(s)) {
return chalk.cyanBright(s);
}
if (state == word && wordCount == 0) {
wordCount++;
return chalk.greenBright(s);
}
if (state == syntax) {
wordCount = 0;
return chalk.cyanBright(s);
}
if (state == dollar)
return chalk.yellowBright(s);
if (state?.name.startsWith('str'))
return chalk.yellowBright(s);
return s;
const next = ch == "EOF" ? void 0 : state();
if (next != state) {
out += style(state, buf);
buf = "";
}
function isSyntax(ch) {
return '()[]{}<>;:+|&='.includes(ch);
state = next == root ? next() : next;
buf += ch;
}
function style(state2, s) {
if (s == "")
return "";
if (reservedWords.includes(s)) {
return chalk.cyanBright(s);
}
function root() {
if (/\s/.test(ch))
return space;
if (isSyntax(ch))
return syntax;
if (/[$]/.test(ch))
return dollar;
if (/["]/.test(ch))
return strDouble;
if (/[']/.test(ch))
return strSingle;
return word;
if (state2 == word && wordCount == 0) {
wordCount++;
return chalk.greenBright(s);
}
function space() {
if (/\s/.test(ch))
return space;
return root;
if (state2 == syntax) {
wordCount = 0;
return chalk.cyanBright(s);
}
function word() {
if (/[0-9a-z/_.]/i.test(ch))
return word;
return root;
}
function syntax() {
if (isSyntax(ch))
return syntax;
return root;
}
function dollar() {
if (/[']/.test(ch))
return str;
return root;
}
function str() {
if (/[']/.test(ch))
return strEnd;
if (/[\\]/.test(ch))
return strBackslash;
return str;
}
function strBackslash() {
return strEscape;
}
function strEscape() {
return str;
}
function strDouble() {
if (/["]/.test(ch))
return strEnd;
return strDouble;
}
function strSingle() {
if (/[']/.test(ch))
return strEnd;
return strSingle;
}
function strEnd() {
return root;
}
return out + '\n';
if (state2 == dollar)
return chalk.yellowBright(s);
if (state2?.name.startsWith("str"))
return chalk.yellowBright(s);
return s;
}
function isSyntax(ch2) {
return "()[]{}<>;:+|&=".includes(ch2);
}
function root() {
if (/\s/.test(ch))
return space;
if (isSyntax(ch))
return syntax;
if (/[$]/.test(ch))
return dollar;
if (/["]/.test(ch))
return strDouble;
if (/[']/.test(ch))
return strSingle;
return word;
}
function space() {
if (/\s/.test(ch))
return space;
return root;
}
function word() {
if (/[0-9a-z/_.]/i.test(ch))
return word;
return root;
}
function syntax() {
if (isSyntax(ch))
return syntax;
return root;
}
function dollar() {
if (/[']/.test(ch))
return str;
return root;
}
function str() {
if (/[']/.test(ch))
return strEnd;
if (/[\\]/.test(ch))
return strBackslash;
return str;
}
function strBackslash() {
return strEscape;
}
function strEscape() {
return str;
}
function strDouble() {
if (/["]/.test(ch))
return strEnd;
return strDouble;
}
function strSingle() {
if (/[']/.test(ch))
return strEnd;
return strSingle;
}
function strEnd() {
return root;
}
return out + "\n";
}
const reservedWords = [
'if',
'then',
'else',
'elif',
'fi',
'case',
'esac',
'for',
'select',
'while',
'until',
'do',
'done',
'in',
var reservedWords = [
"if",
"then",
"else",
"elif",
"fi",
"case",
"esac",
"for",
"select",
"while",
"until",
"do",
"done",
"in"
];
function getCallerLocation(err = new Error()) {
return getCallerLocationFromString(err.stack);
}
function getCallerLocationFromString(stackString = "unknown") {
return stackString.split(/^\s*(at\s)?/m).filter((s) => s?.includes(":"))[2]?.trim() || stackString;
}
export {
errnoMessage,
exitCodeInfo,
formatCmd,
getCallerLocation,
getCallerLocationFromString,
isString,
noop,
noquote,
normalizeMultilinePieces,
parseDuration,
quote,
quotePowerShell,
randomId
};
{
"name": "zx",
"version": "7.2.2",
"description": "A tool for writing better scripts.",
"version": "7.2.3-dev.0844b88",
"description": "A tool for writing better scripts",
"type": "module",

@@ -16,5 +16,2 @@ "main": "./build/index.js",

],
"experimental": [
"./build/experimental.d.ts"
],
"cli": [

@@ -31,3 +28,2 @@ "./build/cli.d.ts"

"./globals": "./build/globals.js",
"./experimental": "./build/experimental.js",
"./cli": "./build/cli.js",

@@ -46,37 +42,47 @@ "./core": "./build/core.js",

"fmt:check": "prettier --check .",
"build": "tsc --project tsconfig.prod.json",
"build": "npm run build:js && npm run build:dts",
"build:check": "tsc",
"test": "npm run build && uvu test -i fixtures",
"build:js": "node scripts/build-js.mjs --format=esm --entry=src/*.ts && npm run build:vendor",
"build:vendor": "node scripts/build-js.mjs --format=esm --entry=src/vendor.ts --bundle=all --banner",
"build:dts": "tsc --project tsconfig.prod.json && node scripts/build-dts.mjs",
"test": "npm run build && npm run test:unit && npm run test:types",
"test:unit": "node ./test/all.test.js",
"test:types": "tsd",
"coverage": "c8 --check-coverage npm test -- -i package",
"mutation": "stryker run",
"test:smoke:bun": "bun test ./test/smoke/bun.test.js",
"test:smoke:win32": "node ./test/smoke/win32.test.js",
"coverage": "c8 -x build/vendor.js -x 'test/**' -x scripts --check-coverage npm test",
"circular": "madge --circular src/*",
"version": "cat package.json | fx .version"
},
"dependencies": {
"@types/fs-extra": "^11.0.1",
"@types/minimist": "^1.2.2",
"@types/node": "^18.16.3",
"@types/ps-tree": "^1.1.2",
"@types/which": "^3.0.0",
"chalk": "^5.2.0",
"fs-extra": "^11.1.1",
"optionalDependencies": {
"@types/fs-extra": "^11.0.4",
"@types/node": ">=20.11.30"
},
"devDependencies": {
"@types/fs-extra": "^11.0.4",
"@types/minimist": "^1.2.5",
"@types/node": ">=20.11.30",
"@types/which": "^3.0.3",
"@webpod/ps": "^0.0.0-beta.2",
"@webpod/ingrid": "^0.0.0-beta.3",
"c8": "^9.1.0",
"chalk": "^5.3.0",
"depseek": "^0.4.1",
"dts-bundle-generator": "^9.3.1",
"esbuild": "^0.20.2",
"esbuild-node-externals": "^1.13.0",
"esbuild-plugin-entry-chunks": "^0.1.11",
"fs-extra": "^11.2.0",
"fx": "*",
"globby": "^13.1.4",
"globby": "^14.0.1",
"madge": "^6.1.0",
"minimist": "^1.2.8",
"node-fetch": "3.3.1",
"ps-tree": "^1.2.0",
"webpod": "^0",
"which": "^3.0.0",
"yaml": "^2.2.2"
"node-fetch-native": "^1.6.4",
"prettier": "^3.2.5",
"tsd": "^0.30.7",
"typescript": "^5.4.3",
"which": "^4.0.0",
"yaml": "^2.4.1",
"zurk": "^0.0.32"
},
"devDependencies": {
"@stryker-mutator/core": "^6.4.2",
"c8": "^7.13.0",
"madge": "^6.0.0",
"prettier": "^2.8.8",
"tsd": "^0.28.1",
"typescript": "^5.0.4",
"uvu": "^0.5.6"
},
"publishConfig": {

@@ -92,3 +98,4 @@ "registry": "https://wombat-dressing-room.appspot.com"

"singleQuote": true,
"endOfLine": "lf"
"endOfLine": "lf",
"trailingComma": "es5"
},

@@ -95,0 +102,0 @@ "repository": "google/zx",

@@ -1,2 +0,2 @@

# 🐚 zx
<h1><img src="https://google.github.io/zx/img/logo.svg" alt="Zx logo" height="32" valign="middle"> zx</h1>

@@ -31,570 +31,9 @@ ```js

```bash
npm i -g zx
npm install zx
```
**Requirement**: Node version >= 16.0.0
## Goods
[$](#command-) · [cd()](#cd) · [fetch()](#fetch) · [question()](#question) · [sleep()](#sleep) · [echo()](#echo) · [stdin()](#stdin) · [within()](#within) · [retry()](#retry) · [spinner()](#spinner) ·
[chalk](#chalk-package) · [fs](#fs-package) · [os](#os-package) · [path](#path-package) · [glob](#globby-package) · [yaml](#yaml-package) · [minimist](#minimist-package) · [which](#which-package) ·
[__filename](#__filename--__dirname) · [__dirname](#__filename--__dirname) · [require()](#require)
For running commands on remote hosts,
see [webpod](https://github.com/webpod/webpod).
## Documentation
Write your scripts in a file with an `.mjs` extension in order to
use `await` at the top level. If you prefer the `.js` extension,
wrap your scripts in something like `void async function () {...}()`.
Read documentation on [google.github.io/zx](https://google.github.io/zx/).
Add the following shebang to the beginning of your `zx` scripts:
```bash
#!/usr/bin/env zx
```
Now you will be able to run your script like so:
```bash
chmod +x ./script.mjs
./script.mjs
```
Or via the `zx` executable:
```bash
zx ./script.mjs
```
All functions (`$`, `cd`, `fetch`, etc) are available straight away
without any imports.
Or import globals explicitly (for better autocomplete in VS Code).
```js
import 'zx/globals'
```
### ``$`command` ``
Executes a given command using the `spawn` func
and returns [`ProcessPromise`](#processpromise).
Everything passed through `${...}` will be automatically escaped and quoted.
```js
let name = 'foo & bar'
await $`mkdir ${name}`
```
**There is no need to add extra quotes.** Read more about it in
[quotes](docs/quotes.md).
You can pass an array of arguments if needed:
```js
let flags = [
'--oneline',
'--decorate',
'--color',
]
await $`git log ${flags}`
```
If the executed program returns a non-zero exit code,
[`ProcessOutput`](#processoutput) will be thrown.
```js
try {
await $`exit 1`
} catch (p) {
console.log(`Exit code: ${p.exitCode}`)
console.log(`Error: ${p.stderr}`)
}
```
### `ProcessPromise`
```ts
class ProcessPromise extends Promise<ProcessOutput> {
stdin: Writable
stdout: Readable
stderr: Readable
exitCode: Promise<number>
pipe(dest): ProcessPromise
kill(): Promise<void>
nothrow(): this
quiet(): this
}
```
Read more about the [ProcessPromise](docs/process-promise.md).
### `ProcessOutput`
```ts
class ProcessOutput {
readonly stdout: string
readonly stderr: string
readonly signal: string
readonly exitCode: number
toString(): string // Combined stdout & stderr.
}
```
The output of the process is captured as-is. Usually, programs print a new
line `\n` at the end.
If `ProcessOutput` is used as an argument to some other `$` process,
**zx** will use stdout and trim the new line.
```js
let date = await $`date`
await $`echo Current date is ${date}.`
```
## Functions
### `cd()`
Changes the current working directory.
```js
cd('/tmp')
await $`pwd` // => /tmp
```
### `fetch()`
A wrapper around the [node-fetch](https://www.npmjs.com/package/node-fetch)
package.
```js
let resp = await fetch('https://medv.io')
```
### `question()`
A wrapper around the [readline](https://nodejs.org/api/readline.html) package.
```js
let bear = await question('What kind of bear is best? ')
```
### `sleep()`
A wrapper around the `setTimeout` function.
```js
await sleep(1000)
```
### `echo()`
A `console.log()` alternative which can take [ProcessOutput](#processoutput).
```js
let branch = await $`git branch --show-current`
echo`Current branch is ${branch}.`
// or
echo('Current branch is', branch)
```
### `stdin()`
Returns the stdin as a string.
```js
let content = JSON.parse(await stdin())
```
### `within()`
Creates a new async context.
```js
await $`pwd` // => /home/path
within(async () => {
cd('/tmp')
setTimeout(async () => {
await $`pwd` // => /tmp
}, 1000)
})
await $`pwd` // => /home/path
```
```js
let version = await within(async () => {
$.prefix += 'export NVM_DIR=$HOME/.nvm; source $NVM_DIR/nvm.sh; '
await $`nvm use 16`
return $`node -v`
})
```
### `retry()`
Retries a callback for a few times. Will return after the first
successful attempt, or will throw after specifies attempts count.
```js
let p = await retry(10, () => $`curl https://medv.io`)
// With a specified delay between attempts.
let p = await retry(20, '1s', () => $`curl https://medv.io`)
// With an exponential backoff.
let p = await retry(30, expBackoff(), () => $`curl https://medv.io`)
```
### `spinner()`
Starts a simple CLI spinner.
```js
await spinner(() => $`long-running command`)
// With a message.
await spinner('working...', () => $`sleep 99`)
```
## Packages
The following packages are available without importing inside scripts.
### `chalk` package
The [chalk](https://www.npmjs.com/package/chalk) package.
```js
console.log(chalk.blue('Hello world!'))
```
### `fs` package
The [fs-extra](https://www.npmjs.com/package/fs-extra) package.
```js
let {version} = await fs.readJson('./package.json')
```
### `os` package
The [os](https://nodejs.org/api/os.html) package.
```js
await $`cd ${os.homedir()} && mkdir example`
```
### `path` package
The [path](https://nodejs.org/api/path.html) package.
```js
await $`mkdir ${path.join(basedir, 'output')}`
```
### `globby` package
The [globby](https://github.com/sindresorhus/globby) package.
```js
let packages = await glob(['package.json', 'packages/*/package.json'])
```
### `yaml` package
The [yaml](https://www.npmjs.com/package/yaml) package.
```js
console.log(YAML.parse('foo: bar').foo)
```
### `minimist` package
The [minimist](https://www.npmjs.com/package/minimist) package available
as global const `argv`.
```js
if (argv.someFlag) {
echo('yes')
}
```
### `which` package
The [which](https://github.com/npm/node-which) package.
```js
let node = await which('node')
```
## Configuration
### `$.shell`
Specifies what shell is used. Default is `which bash`.
```js
$.shell = '/usr/bin/bash'
```
Or use a CLI argument: `--shell=/bin/bash`
### `$.spawn`
Specifies a `spawn` api. Defaults to `require('child_process').spawn`.
### `$.prefix`
Specifies the command that will be prefixed to all commands run.
Default is `set -euo pipefail;`.
Or use a CLI argument: `--prefix='set -e;'`
### `$.quote`
Specifies a function for escaping special characters during
command substitution.
### `$.verbose`
Specifies verbosity. Default is `true`.
In verbose mode, `zx` prints all executed commands alongside with their
outputs.
Or use the CLI argument `--quiet` to set `$.verbose = false`.
### `$.env`
Specifies an environment variables map.
Defaults to `process.env`.
### `$.cwd`
Specifies a current working directory of all processes created with the `$`.
The [cd()](#cd) func changes only `process.cwd()` and if no `$.cwd` specified,
all `$` processes use `process.cwd()` by default (same as `spawn` behavior).
### `$.log`
Specifies a [logging function](src/core.ts).
```ts
import { LogEntry, log } from 'zx/core'
$.log = (entry: LogEntry) => {
switch (entry.kind) {
case 'cmd':
// for example, apply custom data masker for cmd printing
process.stderr.write(masker(entry.cmd))
break
default:
log(entry)
}
}
```
## Polyfills
### `__filename` & `__dirname`
In [ESM](https://nodejs.org/api/esm.html) modules, Node.js does not provide
`__filename` and `__dirname` globals. As such globals are really handy in
scripts,
`zx` provides these for use in `.mjs` files (when using the `zx` executable).
### `require()`
In [ESM](https://nodejs.org/api/modules.html#modules_module_createrequire_filename)
modules, the `require()` function is not defined.
The `zx` provides `require()` function, so it can be used with imports in `.mjs`
files (when using `zx` executable).
```js
let {version} = require('./package.json')
```
## FAQ
### Passing env variables
```js
process.env.FOO = 'bar'
await $`echo $FOO`
```
### Passing array of values
When passing an array of values as an argument to `$`, items of the array will
be escaped
individually and concatenated via space.
Example:
```js
let files = [...]
await $`tar cz ${files}`
```
### Importing into other scripts
It is possible to make use of `$` and other functions via explicit imports:
```js
#!/usr/bin/env node
import { $ } from 'zx'
await $`date`
```
### Scripts without extensions
If script does not have a file extension (like `.git/hooks/pre-commit`), zx
assumes that it is
an [ESM](https://nodejs.org/api/modules.html#modules_module_createrequire_filename)
module.
### Markdown scripts
The `zx` can execute [scripts written as markdown](docs/markdown.md):
```bash
zx docs/markdown.md
```
### TypeScript scripts
```ts
import { $ } from 'zx'
// Or
import 'zx/globals'
void async function () {
await $`ls -la`
}()
```
Set [`"type": "module"`](https://nodejs.org/api/packages.html#packages_type)
in **package.json**
and [`"module": "ESNext"`](https://www.typescriptlang.org/tsconfig/#module)
in **tsconfig.json**.
### Executing remote scripts
If the argument to the `zx` executable starts with `https://`, the file will be
downloaded and executed.
```bash
zx https://medv.io/game-of-life.js
```
### Executing scripts from stdin
The `zx` supports executing scripts from stdin.
```js
zx << 'EOF'
await $`pwd`
EOF
```
### Executing scripts via --eval
Evaluate the following argument as a script.
```bash
cat package.json | zx --eval 'let v = JSON.parse(await stdin()).version; echo(v)'
```
### Installing dependencies via --install
```js
// script.mjs:
import sh from 'tinysh'
sh.say('Hello, world!')
```
Add `--install` flag to the `zx` command to install missing dependencies
automatically.
```bash
zx --install script.mjs
```
You can also specify needed version by adding comment with `@` after
the import.
```js
import sh from 'tinysh' // @^1
```
### Executing commands on remote hosts
The `zx` uses [webpod](https://github.com/webpod/webpod) to execute commands on
remote hosts.
```js
import { ssh } from 'zx'
await ssh('user@host')`echo Hello, world!`
```
### Attaching a profile
By default `child_process` does not include aliases and bash functions.
But you are still able to do it by hand. Just attach necessary directives
to the `$.prefix`.
```js
$.prefix += 'export NVM_DIR=$HOME/.nvm; source $NVM_DIR/nvm.sh; '
await $`nvm -v`
```
### Using GitHub Actions
The default GitHub Action runner comes with `npx` installed.
```yaml
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build
env:
FORCE_COLOR: 3
run: |
npx zx <<'EOF'
await $`...`
EOF
```
### Canary / Beta / RC builds
Impatient early adopters can try the experimental zx versions.
But keep in mind: these builds are ⚠️️__beta__ in every sense.
```bash
npm i zx@dev
npx zx@dev --install --quiet <<< 'import _ from "lodash" /* 4.17.15 */; console.log(_.VERSION)'
```
## License

@@ -601,0 +40,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc