Skip to content

Instantly share code, notes, and snippets.

@bdombro
Last active March 10, 2022 17:02
Show Gist options
  • Save bdombro/09da1431197a69acb20a68bdee5adf9d to your computer and use it in GitHub Desktop.
Save bdombro/09da1431197a69acb20a68bdee5adf9d to your computer and use it in GitHub Desktop.
A template/starter for a zx script
#!/usr/bin/env zx
// eslint-disable-next-line import/no-unresolved
// import { startSpinner } from 'zx/experimental';
const about = `
/**
* myscript.mjs
* - what i do
* - why i do
* - how i do
*
*/
npx zx myscript.mjs <arg1> <arg2> [--verbose] [--silent]
`;
export default async function main(props) {
const { arg1, flag1 } = parseProps(props);
try {
await log.announce('SCRIPT:START');
// Put the top level code here
// Use spinners to loading
// const stopSpinner = startSpinner();
// throw HandledError to avoid dumping stack traces
await log();
await log.announce('SCRIPT:DONE');
} catch (e) {
await log.say('script failed');
if (e instanceof HandledError) await log.error(e.message);
else throw e;
}
}
function parseProps(props) {
const { _, flag1, verbose, silent, ...rest } = props;
const arg1 = _?.[1]
if (!arg1 || Object.keys(rest).length) {
exitWithHelp();
}
return { arg1, flag1, verbose, silent };
}
function exitWithHelp() {
log(about);
process.exit(1);
}
// If script is being called directly, run it
const isCalled =
process.argv[2].split('/').pop() === import.meta.url.split('/').pop();
if (isCalled) {
$.verbose = argv.verbose;
$.silent = argv.silent;
if (!globalThis.$) {
throwError('This script must be ran using zx (https://github.com/google/zx)');
}
main(argv);
}
/**
* Util/Lib functions for zx scripts
*/
export class HandledError extends Error {}
export function throwHandledError(message) {
throw new HandledError(message);
}
export function throwError(message) {
throw new Error(message);
}
/**
* Pretty logs
* - has a shouldReplaceLast option to replace the former line
* - log.info accepts an array and will print each array item on a new line
*/
export function log(msg, shouldReplaceLast) {
return shouldReplaceLast
? process.stdout.write(`${msg}\r`)
: console.log(msg);
}
log.info = async (msg, shouldReplaceLast) => {
const msgArray = Array.isArray(msg) ? msg : [msg];
for (const m of msgArray) {
await log(chalk.gray(' - ' + m), shouldReplaceLast);
}
};
log.announce = (msg, shouldReplaceLast) =>
log(chalk.black.bgGreen(msg), shouldReplaceLast);
log.warn = (msg, shouldReplaceLast) =>
log(chalk.white.bgOrange(msg), shouldReplaceLast);
log.error = (msg, shouldReplaceLast) =>
log(chalk.white.bgRed(msg), shouldReplaceLast);
log.newLine = () => log('');
log.say = async msg => !$.silent && $`say ${msg}`;
// Wraps a fn with logging disabled
async function silenced(fn) {
return async () => {
const verboseBefore = $.verbose;
$.verbose = false;
await promisify(fn)();
$.verbose = verboseBefore;
};
}
function promisify(fn) {
return async () => fn();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment