diff --git a/packages/jsdoc-cli/lib/engine.js b/packages/jsdoc-cli/lib/engine.js index 4e2b27e5..1f7b9e5c 100644 --- a/packages/jsdoc-cli/lib/engine.js +++ b/packages/jsdoc-cli/lib/engine.js @@ -15,7 +15,7 @@ */ /* eslint-disable no-process-exit */ -import { Api } from '@jsdoc/core'; +import { Api, config as jsdocConfig } from '@jsdoc/core'; import { getLogFunctions } from '@jsdoc/util'; import _ from 'lodash'; import ow from 'ow'; @@ -37,6 +37,9 @@ function validateChoice(flagInfo, choices, values) { } } +const FATAL_ERROR_MESSAGE = + 'Exiting JSDoc because an error occurred. See the previous log messages for details.'; + /** * `KNOWN_FLAGS` is a set of all known flag names, including the long and short forms. * @@ -225,6 +228,7 @@ export default class Engine { return `Options:\n${help({ maxLength })}\n\nVisit https://jsdoc.app/ for more information.`; } + // TODO: Add a typedef for this. /** * Details about the command-line flags that JSDoc recognizes. */ @@ -232,6 +236,40 @@ export default class Engine { return flags; } + // TODO: Add details about the directory and filenames that this method looks for. + /** + * Parses command-line flags; loads the JSDoc configuration file; and adds configuration details + * to the JSDoc environment. + * + * For details about supported command-line flags, see the value of the + * {@link module:@jsdoc/cli#knownFlags} property. + * + * @returns {Promise} A promise that is fulfilled after the configuration is loaded. + */ + loadConfig() { + const { env } = this; + + try { + env.opts = _.defaults({}, this.parseFlags(env.args), env.opts); + } catch (e) { + this.shouldPrintHelp = true; + this.exit(1, `${e.message}\n`); + + return Promise.reject(e); + } + + return jsdocConfig.load(env.opts.configure).then( + (conf) => { + env.conf = conf.config; + // Look for options on the command line, then in the config. + env.opts = _.defaults(env.opts, env.conf.opts); + }, + (e) => { + this.exit(1, `Cannot parse the config file: ${e}\n${FATAL_ERROR_MESSAGE}`); + } + ); + } + /** * The log level to use. Messages are logged only if they are at or above this level. * Must be an enumerated value of {@link module:@jsdoc/cli.LOG_LEVELS}. diff --git a/packages/jsdoc-cli/test/specs/lib/engine.js b/packages/jsdoc-cli/test/specs/lib/engine.js index a0c4a14e..a3ee5f64 100644 --- a/packages/jsdoc-cli/test/specs/lib/engine.js +++ b/packages/jsdoc-cli/test/specs/lib/engine.js @@ -14,7 +14,10 @@ limitations under the License. */ +/* global jsdoc */ + import EventEmitter from 'node:events'; +import path from 'node:path'; import Engine from '../../../lib/engine.js'; import flags from '../../../lib/flags.js'; @@ -265,6 +268,61 @@ describe('@jsdoc/cli/lib/engine', () => { }); }); + describe('loadConfig', () => { + const configPath = path.resolve( + path.join(jsdoc.dirname(import.meta.url), '../../fixtures/configs/conf.json') + ); + let instance; + + beforeEach(() => { + instance = new Engine(); + instance.env.options.configure = configPath; + }); + + it('parses the command-line flags', async () => { + instance.env.args = ['-p', '-v']; + + await instance.loadConfig(); + + expect(instance.env.options.private).toBeTrue(); + expect(instance.env.options.version).toBeTrue(); + }); + + it('exits if the command-line flags cannot be parsed', async () => { + instance.env.args = ['--not-a-real-flag']; + + spyOn(instance, 'exit'); + try { + await instance.loadConfig(); + + // We shouldn't get here. + expect(false).toBeTrue(); + } catch (e) { + // Expected exit code. + expect(instance.exit.calls.argsFor(0)[0]).toBe(1); + // Expected error message. + expect(instance.exit.calls.argsFor(0)[1]).toContain( + 'Unknown command-line option: --not-a-real-flag' + ); + } + }); + + it('adds the config info to the JSDoc environment', async () => { + await instance.loadConfig(); + + expect(instance.env.config.sourceType).toBe('script'); + }); + + it('merges the command-line flags from the config file with the real flags', async () => { + instance.env.args = ['-p']; + + await instance.loadConfig(); + + expect(instance.env.options.private).toBeTrue(); + expect(instance.env.options.version).toBeTrue(); + }); + }); + describe('LOG_LEVELS', () => { it('is lib/logger.LEVELS', () => { expect(Engine.LOG_LEVELS).toBe(LEVELS); diff --git a/packages/jsdoc/cli.js b/packages/jsdoc/cli.js index cac2a0c2..80efafec 100644 --- a/packages/jsdoc/cli.js +++ b/packages/jsdoc/cli.js @@ -20,11 +20,10 @@ import { readFile } from 'node:fs/promises'; import { fileURLToPath } from 'node:url'; import Engine from '@jsdoc/cli'; -import { config as jsdocConfig, plugins } from '@jsdoc/core'; +import { plugins } from '@jsdoc/core'; import { augment, Package, resolveBorrows } from '@jsdoc/doclet'; import { createParser, handlers } from '@jsdoc/parse'; import { Dictionary } from '@jsdoc/tag'; -import _ from 'lodash'; import stripBom from 'strip-bom'; import stripJsonComments from 'strip-json-comments'; @@ -47,8 +46,6 @@ export default (() => { const cli = {}; const engine = new Engine(); const { api, env, log } = engine; - const FATAL_ERROR_MESSAGE = - 'Exiting JSDoc because an error occurred. See the previous log messages for details.'; // TODO: docs cli.setVersionInfo = () => { @@ -69,31 +66,7 @@ export default (() => { }; // TODO: docs - cli.loadConfig = async () => { - try { - env.opts = engine.parseFlags(env.args); - } catch (e) { - engine.shouldPrintHelp = true; - engine.exit(1, `${e.message}\n`); - - return cli; - } - - try { - // eslint-disable-next-line require-atomic-updates - env.conf = (await jsdocConfig.load(env.opts.configure)).config; - } catch (e) { - engine.exit(1, `Cannot parse the config file: ${e}\n${FATAL_ERROR_MESSAGE}`); - - return cli; - } - - // Look for options on the command line, then in the config. - env.opts = _.defaults(env.opts, env.conf.opts); - env.tags = Dictionary.fromConfig(env); - - return cli; - }; + cli.loadConfig = () => engine.loadConfig(); // TODO: docs cli.configureLogger = () => { @@ -130,6 +103,9 @@ export default (() => { let cmd; const { options } = env; + // TODO: Move to `Api`. + env.tags = Dictionary.fromConfig(env); + // If we already need to exit with an error, don't do any more work. if (engine.shouldExitWithError) { cmd = () => Promise.resolve(0);