diff options
Diffstat (limited to 'src/util/cli.js')
-rw-r--r-- | src/util/cli.js | 161 |
1 files changed, 123 insertions, 38 deletions
diff --git a/src/util/cli.js b/src/util/cli.js index f1a3190..ce513f0 100644 --- a/src/util/cli.js +++ b/src/util/cli.js @@ -17,7 +17,7 @@ export const ENABLE_COLOR = const C = (n) => ENABLE_COLOR ? (text) => `\x1b[${n}m${text}\x1b[0m` : (text) => text; -export const color = { +export const colors = { bright: C('1'), dim: C('2'), normal: C('22'), @@ -64,8 +64,10 @@ export async function parseOptions(options, optionDescriptorMap) { // options is the array of options you want to process; // optionDescriptorMap is a mapping of option names to objects that describe // the expected value for their corresponding options. - // Returned is a mapping of any specified option names to their values, or - // a process.exit(1) and error message if there were any issues. + // + // Returned is... + // - a mapping of any specified option names to their values + // - a process.exit(1) and error message if there were any issues // // Here are examples of optionDescriptorMap to cover all the things you can // do with it: @@ -95,11 +97,10 @@ export async function parseOptions(options, optionDescriptorMap) { // ['--directory', 'apple'] -> {'directory': 'apple'} // ['--directory', 'artichoke'] -> (error) // ['--files', 'a', 'b', 'c', ';'] -> {'files': ['a', 'b', 'c']} - // - // TODO: Be able to validate the values in a series option. const handleDashless = optionDescriptorMap[parseOptions.handleDashless]; const handleUnknown = optionDescriptorMap[parseOptions.handleUnknown]; + const result = Object.create(null); for (let i = 0; i < options.length; i++) { const option = options[i]; @@ -107,6 +108,7 @@ export async function parseOptions(options, optionDescriptorMap) { // --x can be a flag or expect a value or series of values let name = option.slice(2).split('=')[0]; // '--x'.split('=') = ['--x'] let descriptor = optionDescriptorMap[name]; + if (!descriptor) { if (handleUnknown) { handleUnknown(option); @@ -116,36 +118,49 @@ export async function parseOptions(options, optionDescriptorMap) { } continue; } + if (descriptor.alias) { name = descriptor.alias; descriptor = optionDescriptorMap[name]; } - if (descriptor.type === 'flag') { - result[name] = true; - } else if (descriptor.type === 'value') { - let value = option.slice(2).split('=')[1]; - if (!value) { - value = options[++i]; - if (!value || value.startsWith('-')) { - value = null; - } + + switch (descriptor.type) { + case 'flag': { + result[name] = true; + break; } - if (!value) { - console.error(`Expected a value for --${name}`); - process.exit(1); + + case 'value': { + let value = option.slice(2).split('=')[1]; + if (!value) { + value = options[++i]; + if (!value || value.startsWith('-')) { + value = null; + } + } + + if (!value) { + console.error(`Expected a value for --${name}`); + process.exit(1); + } + + result[name] = value; + break; } - result[name] = value; - } else if (descriptor.type === 'series') { - if (!options.slice(i).includes(';')) { - console.error( - `Expected a series of values concluding with ; (\\;) for --${name}` - ); - process.exit(1); + + case 'series': { + if (!options.slice(i).includes(';')) { + console.error(`Expected a series of values concluding with ; (\\;) for --${name}`); + process.exit(1); + } + + const endIndex = i + options.slice(i).indexOf(';'); + result[name] = options.slice(i + 1, endIndex); + i = endIndex; + break; } - const endIndex = i + options.slice(i).indexOf(';'); - result[name] = options.slice(i + 1, endIndex); - i = endIndex; } + if (descriptor.validate) { const validation = await descriptor.validate(result[name]); if (validation !== true) { @@ -167,10 +182,12 @@ export async function parseOptions(options, optionDescriptorMap) { } continue; } + if (descriptor.alias) { name = descriptor.alias; descriptor = optionDescriptorMap[name]; } + if (descriptor.type === 'flag') { result[name] = true; } else { @@ -198,12 +215,30 @@ export function decorateTime(arg1, arg2) { timeSpent: 0, timesCalled: 0, displayTime() { - const averageTime = meta.timeSpent / meta.timesCalled; + const align1 = 48; + const align2 = 22; + + const averageTime = (meta.timeSpent / meta.timesCalled).toExponential(1); + const idPart = typeof id === 'symbol' ? id.description : id; + const timePart = `${meta.timeSpent} ms / ${meta.timesCalled} calls`; + const avgPart = `(avg: ${averageTime} ms)`; + + const alignPart1 = + (idPart.length >= align1 + ? ' ' + : ' ' + '.'.repeat(Math.max(0, align1 - 2 - idPart.length)) + ' '); + + const alignPart2 = + (timePart.length >= align2 + ? ' ' + : ' '.repeat(Math.max(0, align2 - timePart.length))); + console.log( - `\x1b[1m${typeof id === 'symbol' ? id.description : id}(...):\x1b[0m ${ - meta.timeSpent - } ms / ${meta.timesCalled} calls \x1b[2m(avg: ${averageTime} ms)\x1b[0m` - ); + colors.bright(idPart) + + alignPart1 + + timePart + + alignPart2 + + colors.dim(avgPart)); }, }; @@ -211,7 +246,7 @@ export function decorateTime(arg1, arg2) { const fn = function (...args) { const start = Date.now(); - const ret = functionToBeWrapped(...args); + const ret = functionToBeWrapped.apply(this, args); const end = Date.now(); meta.timeSpent += end - start; meta.timesCalled++; @@ -233,11 +268,20 @@ decorateTime.displayTime = function () { ...Object.getOwnPropertyNames(map), ]; - if (keys.length) { - console.log(`\x1b[1mdecorateTime results: ` + '-'.repeat(40) + '\x1b[0m'); - for (const key of keys) { - map[key].displayTime(); - } + if (!keys.length) { + return; + } + + console.log(`\x1b[1mdecorateTime results: ` + '-'.repeat(40) + '\x1b[0m'); + + const metas = + keys + .map(key => map[key]) + .filter(meta => meta.timeSpent >= 1) // Milliseconds! + .sort((a, b) => a.timeSpent - b.timeSpent); + + for (const meta of metas) { + meta.displayTime(); } }; @@ -313,3 +357,44 @@ export function progressCallAll(msgOrMsgFn, array) { return vals; } + +export function fileIssue({ + topMessage = `This shouldn't happen.`, +} = {}) { + if (topMessage) { + console.error(colors.red(`${topMessage} Please let the HSMusic developers know:`)); + } + console.error(colors.red(`- https://hsmusic.wiki/feedback/`)); + console.error(colors.red(`- https://github.com/hsmusic/hsmusic-wiki/issues/`)); +} + +export async function logicalCWD() { + if (process.env.PWD) { + return process.env.PWD; + } + + const {exec} = await import('node:child_process'); + const {stat} = await import('node:fs/promises'); + + try { + await stat('/bin/sh'); + } catch (error) { + // Not logical, so sad. + return process.cwd(); + } + + const proc = exec('/bin/pwd -L'); + + let output = ''; + proc.stdout.on('data', buf => { output += buf; }); + + await new Promise(resolve => proc.on('exit', resolve)); + + return output.trim(); +} + +export async function logicalPathTo(target) { + const {relative} = await import('node:path'); + const cwd = await logicalCWD(); + return relative(cwd, target); +} |