diff --git a/bin/cli.mjs b/bin/cli.mjs index c5019de9..ecbbecbf 100755 --- a/bin/cli.mjs +++ b/bin/cli.mjs @@ -6,6 +6,7 @@ import { Argument, Command, Option } from 'commander'; import interactive from './commands/interactive.mjs'; import list, { types } from './commands/list.mjs'; import commands from './commands/index.mjs'; +import { errorWrap } from './utils.mjs'; const program = new Command() .name('api-docs-tooling') @@ -33,21 +34,21 @@ commands.forEach(({ name, description, options, action }) => { }); // Set the action for the command - cmd.action(action); + cmd.action(errorWrap(action)); }); // Register the interactive command program .command('interactive') .description('Launch guided CLI wizard') - .action(interactive); + .action(errorWrap(interactive)); // Register the list command program .command('list') .addArgument(new Argument('', 'The type to list').choices(types)) .description('List the given type') - .action(list); + .action(errorWrap(list)); // Parse and execute command-line arguments program.parse(process.argv); diff --git a/bin/utils.mjs b/bin/utils.mjs index 45ceff2d..b32a0929 100644 --- a/bin/utils.mjs +++ b/bin/utils.mjs @@ -1,9 +1,23 @@ import createMarkdownLoader from '../src/loaders/markdown.mjs'; import createMarkdownParser from '../src/parsers/markdown.mjs'; -// Instantiate loader and parser once to reuse -const loader = createMarkdownLoader(); -const parser = createMarkdownParser(); +/** + * Generic lazy initializer. + * @template T + * @param {() => T} factory - Function to create the instance. + * @returns {() => T} - A function that returns the singleton instance. + */ +export const lazy = factory => { + let instance; + return () => (instance ??= factory()); +}; + +// Instantiate loader and parser once to reuse, +// but only if/when we actually need them. No need +// to create these objects just to load a different +// utility. +const loader = lazy(createMarkdownLoader); +const parser = lazy(createMarkdownParser); /** * Load and parse markdown API docs. @@ -12,10 +26,27 @@ const parser = createMarkdownParser(); * @returns {Promise} - Parsed documentation objects. */ export async function loadAndParse(input, ignore) { - const files = await loader.loadFiles(input, ignore); - return parser.parseApiDocs(files); + const files = await loader().loadFiles(input, ignore); + return parser().parseApiDocs(files); } +/** + * Wraps a function to catch both synchronous and asynchronous errors. + * + * @param {Function} fn - The function to wrap. Can be synchronous or return a Promise. + * @returns {Function} A new function that handles errors and logs them. + */ +export const errorWrap = + fn => + async (...args) => { + try { + return await fn(...args); + } catch (err) { + console.error(err); + process.exit(1); + } + }; + /** * Represents a command-line option for the linter CLI. * @typedef {Object} Option diff --git a/src/threading/index.mjs b/src/threading/index.mjs index 648192ff..488fa8e3 100644 --- a/src/threading/index.mjs +++ b/src/threading/index.mjs @@ -56,7 +56,11 @@ export default class WorkerPool { this.changeActiveThreadCount(-1); this.processQueue(threads); - (result?.error ? reject : resolve)(result); + if (result?.error) { + reject(result.error); + } else { + resolve(result); + } }); // Handle worker thread errors