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

Commit

Permalink
fix: validate files and ignores elements (#103)
Browse files Browse the repository at this point in the history
* fix: validate `files` and `ignores` elements

* do not validate `files` and `ignores` twice

* fail validation when `files` or `ignores` is set to `undefined`

* `files` and `ignores` validation in `getConfig()`

* Improve message when a config is not an object

* Validation in `normalize` and `normalizeSync`

* `it_` → `localIt`
  • Loading branch information
fasttime authored Aug 29, 2023
1 parent 730d16a commit c40894f
Show file tree
Hide file tree
Showing 4 changed files with 227 additions and 71 deletions.
62 changes: 9 additions & 53 deletions src/base-schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,13 @@
// Helpers
//------------------------------------------------------------------------------

/**
* Assets that a given value is an array.
* @param {*} value The value to check.
* @returns {void}
* @throws {TypeError} When the value is not an array.
*/
function assertIsArray(value) {
if (!Array.isArray(value)) {
throw new TypeError('Expected value to be an array.');
}
}

/**
* Assets that a given value is an array containing only strings and functions.
* @param {*} value The value to check.
* @returns {void}
* @throws {TypeError} When the value is not an array of strings and functions.
*/
function assertIsArrayOfStringsAndFunctions(value, name) {
assertIsArray(value, name);

if (value.some(item => typeof item !== 'string' && typeof item !== 'function')) {
throw new TypeError('Expected array to only contain strings.');
}
}
const NOOP_STRATEGY = {
required: false,
merge() {
return undefined;
},
validate() { }
};

//------------------------------------------------------------------------------
// Exports
Expand All @@ -53,32 +35,6 @@ export const baseSchema = Object.freeze({
}
}
},
files: {
required: false,
merge() {
return undefined;
},
validate(value) {

// first check if it's an array
assertIsArray(value);

// then check each member
value.forEach(item => {
if (Array.isArray(item)) {
assertIsArrayOfStringsAndFunctions(item);
} else if (typeof item !== 'string' && typeof item !== 'function') {
throw new TypeError('Items must be a string, a function, or an array of strings and functions.');
}
});

}
},
ignores: {
required: false,
merge() {
return undefined;
},
validate: assertIsArrayOfStringsAndFunctions
}
files: NOOP_STRATEGY,
ignores: NOOP_STRATEGY
});
28 changes: 18 additions & 10 deletions src/config-array.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import createDebug from 'debug';

import { ObjectSchema } from '@humanwhocodes/object-schema';
import { baseSchema } from './base-schema.js';
import { filesAndIgnoresSchema } from './files-and-ignores-schema.js';

//------------------------------------------------------------------------------
// Helpers
Expand All @@ -30,6 +31,8 @@ const MINIMATCH_OPTIONS = {

const CONFIG_TYPES = new Set(['array', 'function']);

const FILES_AND_IGNORES_SCHEMA = new ObjectSchema(filesAndIgnoresSchema);

/**
* Shorthand for checking if a value is a string.
* @param {any} value The value to check.
Expand All @@ -40,15 +43,23 @@ function isString(value) {
}

/**
* Asserts that the files key of a config object is a nonempty array.
* Asserts that the files and ignores keys of a config object are valid as per base schema.
* @param {object} config The config object to check.
* @returns {void}
* @throws {TypeError} If the files key isn't a nonempty array.
* @throws {TypeError} If the files and ignores keys of a config object are not valid.
*/
function assertNonEmptyFilesArray(config) {
if (!Array.isArray(config.files) || config.files.length === 0) {
throw new TypeError('The files key must be a non-empty array.');
function assertValidFilesAndIgnores(config) {
if (!config || typeof config !== 'object') {
return;
}
const validateConfig = { };
if ('files' in config) {
validateConfig.files = config.files;
}
if ('ignores' in config) {
validateConfig.ignores = config.ignores;
}
FILES_AND_IGNORES_SCHEMA.validate(validateConfig);
}

/**
Expand Down Expand Up @@ -268,9 +279,6 @@ function pathMatches(filePath, basePath, config) {
*/
const relativeFilePath = path.relative(basePath, filePath);

// if files isn't an array, throw an error
assertNonEmptyFilesArray(config);

// match both strings and functions
const match = pattern => {

Expand Down Expand Up @@ -577,6 +585,7 @@ export class ConfigArray extends Array {
const normalizedConfigs = await normalize(this, context, this.extraConfigTypes);
this.length = 0;
this.push(...normalizedConfigs.map(this[ConfigArraySymbol.preprocessConfig].bind(this)));
this.forEach(assertValidFilesAndIgnores);
this[ConfigArraySymbol.isNormalized] = true;

// prevent further changes
Expand All @@ -598,6 +607,7 @@ export class ConfigArray extends Array {
const normalizedConfigs = normalizeSync(this, context, this.extraConfigTypes);
this.length = 0;
this.push(...normalizedConfigs.map(this[ConfigArraySymbol.preprocessConfig].bind(this)));
this.forEach(assertValidFilesAndIgnores);
this[ConfigArraySymbol.isNormalized] = true;

// prevent further changes
Expand Down Expand Up @@ -746,8 +756,6 @@ export class ConfigArray extends Array {
return;
}

assertNonEmptyFilesArray(config);

/*
* If a config has a files pattern ending in /** or /*, and the
* filePath only matches those patterns, then the config is only
Expand Down
85 changes: 85 additions & 0 deletions src/files-and-ignores-schema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/**
* @fileoverview ConfigSchema
* @author Nicholas C. Zakas
*/

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
* Asserts that a given value is an array.
* @param {*} value The value to check.
* @returns {void}
* @throws {TypeError} When the value is not an array.
*/
function assertIsArray(value) {
if (!Array.isArray(value)) {
throw new TypeError('Expected value to be an array.');
}
}

/**
* Asserts that a given value is an array containing only strings and functions.
* @param {*} value The value to check.
* @returns {void}
* @throws {TypeError} When the value is not an array of strings and functions.
*/
function assertIsArrayOfStringsAndFunctions(value, name) {
assertIsArray(value, name);

if (value.some(item => typeof item !== 'string' && typeof item !== 'function')) {
throw new TypeError('Expected array to only contain strings and functions.');
}
}

/**
* Asserts that a given value is a non-empty array.
* @param {*} value The value to check.
* @returns {void}
* @throws {TypeError} When the value is not an array or an empty array.
*/
function assertIsNonEmptyArray(value) {
if (!Array.isArray(value) || value.length === 0) {
throw new TypeError('Expected value to be a non-empty array.');
}
}

//------------------------------------------------------------------------------
// Exports
//------------------------------------------------------------------------------

/**
* The schema for `files` and `ignores` that every ConfigArray uses.
* @type Object
*/
export const filesAndIgnoresSchema = Object.freeze({
files: {
required: false,
merge() {
return undefined;
},
validate(value) {

// first check if it's an array
assertIsNonEmptyArray(value);

// then check each member
value.forEach(item => {
if (Array.isArray(item)) {
assertIsArrayOfStringsAndFunctions(item);
} else if (typeof item !== 'string' && typeof item !== 'function') {
throw new TypeError('Items must be a string, a function, or an array of strings and functions.');
}
});

}
},
ignores: {
required: false,
merge() {
return undefined;
},
validate: assertIsArrayOfStringsAndFunctions
}
});
Loading

0 comments on commit c40894f

Please sign in to comment.