Last active
March 10, 2022 17:02
-
-
Save bdombro/09da1431197a69acb20a68bdee5adf9d to your computer and use it in GitHub Desktop.
A template/starter for a zx script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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