Skip to content

Commit

Permalink
refactor: make config loading asynchronous
Browse files Browse the repository at this point in the history
Workaround for tschaub/mock-fs#377, which causes `fs.readFileSync()` to fail on Node.js >=20.5.0.

Fixes jsdoc#2097.
  • Loading branch information
hegemonic committed Dec 3, 2023
1 parent 3e4f5fc commit a82263f
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 28 deletions.
10 changes: 5 additions & 5 deletions packages/jsdoc-core/lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
*
* @alias module:@jsdoc/core.config
*/
import { cosmiconfigSync, defaultLoaders } from 'cosmiconfig';
import { cosmiconfig, defaultLoaders } from 'cosmiconfig';
import _ from 'lodash';
import stripBom from 'strip-bom';
import stripJsonComments from 'strip-json-comments';
Expand Down Expand Up @@ -94,7 +94,7 @@ function loadYaml(filepath, content) {
return defaultLoaders['.yaml'](filepath, stripBom(content));
}

const explorerSync = cosmiconfigSync(MODULE_NAME, {
const explorer = cosmiconfig(MODULE_NAME, {
cache: false,
loaders: {
'.json': loadJson,
Expand All @@ -113,13 +113,13 @@ const explorerSync = cosmiconfigSync(MODULE_NAME, {
],
});

export function loadSync(filepath) {
export async function load(filepath) {
let loaded;

if (filepath) {
loaded = explorerSync.load(filepath);
loaded = await explorer.load(filepath);
} else {
loaded = explorerSync.search() || {};
loaded = (await explorer.search()) ?? {};
}

return new Config(loaded.filepath, _.defaultsDeep({}, loaded.config, defaults));
Expand Down
40 changes: 20 additions & 20 deletions packages/jsdoc-core/test/specs/lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,94 +31,94 @@ describe('@jsdoc/core/lib/config', () => {
expect(config).toBeObject();
});

describe('loadSync', () => {
describe('load', () => {
it('is a function', () => {
expect(config.loadSync).toBeFunction();
expect(config.load).toBeFunction();
});

it('returns an object with `config` and `filepath` properties', () => {
it('returns an object with `config` and `filepath` properties', async () => {
mockFs({
'conf.json': '{}',
});

const conf = config.loadSync('conf.json');
const conf = await config.load('conf.json');

expect(conf.config).toBeObject();
expect(conf.filepath).toEndWith('conf.json');
});

it('loads settings from the specified filepath if there is one', () => {
it('loads settings from the specified filepath if there is one', async () => {
mockFs({
'conf.json': '{"foo":"bar"}',
});

const conf = config.loadSync('conf.json');
const conf = await config.load('conf.json');

expect(conf.config.foo).toBe('bar');
});

it('finds the config file when no filepath is specified', () => {
it('finds the config file when no filepath is specified', async () => {
mockFs({
'package.json': '{"jsdoc":{"foo":"bar"}}',
});

const conf = config.loadSync();
const conf = await config.load();

expect(conf.config.foo).toBe('bar');
});

it('parses JSON config files that have an extension and contain comments', () => {
it('parses JSON config files that have an extension and contain comments', async () => {
mockFs({
'.jsdocrc.json': '// comment\n{"foo":"bar"}',
});

const conf = config.loadSync();
const conf = await config.load();

expect(conf.config.foo).toBe('bar');
});

it('parses JSON files that start with a BOM', () => {
it('parses JSON files that start with a BOM', async () => {
mockFs({
'.jsdocrc.json': '\uFEFF{"foo":"bar"}',
});

const conf = config.loadSync();
const conf = await config.load();

expect(conf.config.foo).toBe('bar');
});

it('parses YAML files that start with a BOM', () => {
it('parses YAML files that start with a BOM', async () => {
mockFs({
'.jsdocrc.yaml': '\uFEFF{"foo":"bar"}',
});

const conf = config.loadSync();
const conf = await config.load();

expect(conf.config.foo).toBe('bar');
});

it('provides the default config if the user config is an empty object', () => {
it('provides the default config if the user config is an empty object', async () => {
mockFs({
'.jsdocrc.json': '{}',
});

const conf = config.loadSync();
const conf = await config.load();

expect(conf.config).toEqual(config.defaults);
});

it('provides the default config if there is no user config', () => {
const conf = config.loadSync();
it('provides the default config if there is no user config', async () => {
const conf = await config.load();

expect(conf.config).toEqual(config.defaults);
});

it('merges nested defaults with nested user settings as expected', () => {
it('merges nested defaults with nested user settings as expected', async () => {
mockFs({
'.jsdocrc.json': '{"tags":{"foo":"bar"}}',
});

const conf = config.loadSync();
const conf = await config.load();

expect(conf.config.tags.allowUnknownTags).toBe(config.defaults.tags.allowUnknownTags);
expect(conf.config.tags.foo).toBe('bar');
Expand Down
4 changes: 2 additions & 2 deletions packages/jsdoc/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export default (() => {
};

// TODO: docs
cli.loadConfig = () => {
cli.loadConfig = async () => {
const env = dependencies.get('env');

try {
Expand All @@ -98,7 +98,7 @@ export default (() => {
}

try {
env.conf = config.loadSync(env.opts.configure).config;
env.conf = (await config.load(env.opts.configure)).config;
} catch (e) {
cli.exit(1, `Cannot parse the config file: ${e}\n${FATAL_ERROR_MESSAGE}`);

Expand Down
4 changes: 3 additions & 1 deletion packages/jsdoc/jsdoc.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ import cli from './cli.js';

(async () => {
env.args = process.argv.slice(2);
cli.setEnv(env).setVersionInfo().loadConfig().configureLogger().logStart();
cli.setEnv(env).setVersionInfo();
await cli.loadConfig();
cli.configureLogger().logStart();

await cli.runCommand();
})();

0 comments on commit a82263f

Please sign in to comment.