Skip to content
This repository was archived by the owner on Jun 10, 2024. It is now read-only.

Commit be918b6

Browse files
authored
fix: Throw friendly message for non-object configs (#136)
1 parent c1e2b9e commit be918b6

File tree

2 files changed

+81
-20
lines changed

2 files changed

+81
-20
lines changed

src/config-array.js

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,19 @@ class ConfigError extends Error {
5050
* @param {number} index The index of the config object in the array.
5151
* @param {Error} source The source error.
5252
*/
53-
constructor(name, index, source) {
54-
super(`Config ${name}: ${source.message}`, { cause: source });
53+
constructor(name, index, { cause, message }) {
54+
55+
56+
const finalMessage = message || cause.message;
57+
58+
super(`Config ${name}: ${finalMessage}`, { cause });
5559

5660
// copy over custom properties that aren't represented
57-
for (const key of Object.keys(source)) {
58-
if (!(key in this)) {
59-
this[key] = source[key];
61+
if (cause) {
62+
for (const key of Object.keys(cause)) {
63+
if (!(key in this)) {
64+
this[key] = cause[key];
65+
}
6066
}
6167
}
6268

@@ -82,14 +88,13 @@ class ConfigError extends Error {
8288
* @returns {string} The name of the config object.
8389
*/
8490
function getConfigName(config) {
85-
if (typeof config.name === 'string' && config.name) {
91+
if (config && typeof config.name === 'string' && config.name) {
8692
return `"${config.name}"`;
8793
}
8894

8995
return '(unnamed)';
9096
}
9197

92-
9398
/**
9499
* Rethrows a config error with additional information about the config object.
95100
* @param {object} config The config object to get the name of.
@@ -112,19 +117,28 @@ function isString(value) {
112117
}
113118

114119
/**
115-
* Creates a function that asserts that the files and ignores keys
116-
* of a config object are valid as per base schema.
120+
* Creates a function that asserts that the config is valid
121+
* during normalization. This checks that the config is not nullish
122+
* and that files and ignores keys of a config object are valid as per base schema.
117123
* @param {Object} config The config object to check.
118124
* @param {number} index The index of the config object in the array.
119125
* @returns {void}
120126
* @throws {ConfigError} If the files and ignores keys of a config object are not valid.
121127
*/
122-
function assertValidFilesAndIgnores(config, index) {
128+
function assertValidBaseConfig(config, index) {
123129

124-
if (!config || typeof config !== 'object') {
125-
return;
130+
if (config === null) {
131+
throw new ConfigError(getConfigName(config), index, { message: 'Unexpected null config.' });
126132
}
127-
133+
134+
if (config === undefined) {
135+
throw new ConfigError(getConfigName(config), index, { message: 'Unexpected undefined config.' });
136+
}
137+
138+
if (typeof config !== 'object') {
139+
throw new ConfigError(getConfigName(config), index, { message: 'Unexpected non-object config.' });
140+
}
141+
128142
const validateConfig = { };
129143

130144
if ('files' in config) {
@@ -138,7 +152,7 @@ function assertValidFilesAndIgnores(config, index) {
138152
try {
139153
FILES_AND_IGNORES_SCHEMA.validate(validateConfig);
140154
} catch (validationError) {
141-
rethrowConfigError(config, index, validationError);
155+
rethrowConfigError(config, index, { cause: validationError });
142156
}
143157
}
144158

@@ -634,7 +648,7 @@ export class ConfigArray extends Array {
634648
const normalizedConfigs = await normalize(this, context, this.extraConfigTypes);
635649
this.length = 0;
636650
this.push(...normalizedConfigs.map(this[ConfigArraySymbol.preprocessConfig].bind(this)));
637-
this.forEach(assertValidFilesAndIgnores);
651+
this.forEach(assertValidBaseConfig);
638652
this[ConfigArraySymbol.isNormalized] = true;
639653

640654
// prevent further changes
@@ -656,7 +670,7 @@ export class ConfigArray extends Array {
656670
const normalizedConfigs = normalizeSync(this, context, this.extraConfigTypes);
657671
this.length = 0;
658672
this.push(...normalizedConfigs.map(this[ConfigArraySymbol.preprocessConfig].bind(this)));
659-
this.forEach(assertValidFilesAndIgnores);
673+
this.forEach(assertValidBaseConfig);
660674
this[ConfigArraySymbol.isNormalized] = true;
661675

662676
// prevent further changes
@@ -892,7 +906,7 @@ export class ConfigArray extends Array {
892906
try {
893907
return this[ConfigArraySymbol.schema].merge(result, this[index]);
894908
} catch (validationError) {
895-
rethrowConfigError(this[index], index, validationError);
909+
rethrowConfigError(this[index], index, { cause: validationError});
896910
}
897911
}, {}, this);
898912

tests/config-array.test.js

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -416,13 +416,12 @@ describe('ConfigArray', () => {
416416
},
417417
'eslint:reccommended' // typo
418418
], { basePath });
419-
await configs.normalize();
420419

421420
expect(() => {
422-
configs.getConfig(path.resolve(basePath, 'foo.js'));
421+
configs.normalizeSync();
423422
})
424423
.to
425-
.throw('All arguments must be objects.');
424+
.throw('Config (unnamed): Unexpected non-object config.');
426425

427426
});
428427

@@ -433,6 +432,7 @@ describe('ConfigArray', () => {
433432
name: true
434433
}
435434
], { basePath });
435+
436436
await configs.normalize();
437437

438438
expect(() => {
@@ -451,6 +451,7 @@ describe('ConfigArray', () => {
451451
name: true
452452
}
453453
);
454+
454455
await configs.normalize();
455456

456457
expect(() => {
@@ -460,6 +461,52 @@ describe('ConfigArray', () => {
460461
.throw('Config (unnamed): Key "name": Property must be a string.');
461462

462463
});
464+
465+
it('should throw an error when base config is undefined', async () => {
466+
configs = new ConfigArray([undefined], { basePath });
467+
468+
expect(() => {
469+
configs.normalizeSync();
470+
})
471+
.to
472+
.throw('Config (unnamed): Unexpected undefined config.');
473+
474+
});
475+
476+
it('should throw an error when base config is null', async () => {
477+
configs = new ConfigArray([null], { basePath });
478+
479+
expect(() => {
480+
configs.normalizeSync();
481+
})
482+
.to
483+
.throw('Config (unnamed): Unexpected null config.');
484+
485+
});
486+
487+
it('should throw an error when additional config is undefined', async () => {
488+
configs = new ConfigArray([{}], { basePath });
489+
configs.push(undefined);
490+
491+
expect(() => {
492+
configs.normalizeSync();
493+
})
494+
.to
495+
.throw('Config (unnamed): Unexpected undefined config.');
496+
497+
});
498+
499+
it('should throw an error when additional config is null', async () => {
500+
configs = new ConfigArray([{}], { basePath });
501+
configs.push(null);
502+
503+
expect(() => {
504+
configs.normalizeSync();
505+
})
506+
.to
507+
.throw('Config (unnamed): Unexpected null config.');
508+
509+
});
463510
});
464511

465512
describe('ConfigArray members', () => {

0 commit comments

Comments
 (0)