react-styleguidist
Version:
React components style guide generator
176 lines (129 loc) • 5.56 kB
JavaScript
;
exports.__esModule = true;
exports.default = sanitizeConfig;
var _fs = _interopRequireDefault(require("fs"));
var _path = _interopRequireDefault(require("path"));
var _castArray = _interopRequireDefault(require("lodash/castArray"));
var _isBoolean = _interopRequireDefault(require("lodash/isBoolean"));
var _isFunction = _interopRequireDefault(require("lodash/isFunction"));
var _isPlainObject = _interopRequireDefault(require("lodash/isPlainObject"));
var _isString = _interopRequireDefault(require("lodash/isString"));
var _isFinite = _interopRequireDefault(require("lodash/isFinite"));
var _map = _interopRequireDefault(require("lodash/map"));
var _listify = _interopRequireDefault(require("listify"));
var _kleur = _interopRequireDefault(require("kleur"));
var _fastestLevenshtein = require("fastest-levenshtein");
var _typeDetect = _interopRequireDefault(require("type-detect"));
var _glogg = _interopRequireDefault(require("glogg"));
var _qI = require("q-i");
var _error = _interopRequireDefault(require("./error"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const logger = (0, _glogg.default)('rsg');
const typeCheckers = {
number: _isFinite.default,
string: _isString.default,
boolean: _isBoolean.default,
array: Array.isArray,
function: _isFunction.default,
object: _isPlainObject.default,
'file path': _isString.default,
'existing file path': _isString.default,
'directory path': _isString.default,
'existing directory path': _isString.default
};
const typesList = types => (0, _listify.default)(types, {
finalWord: 'or'
});
const shouldBeFile = types => types.some(type => type.includes('file'));
const shouldBeDirectory = types => types.some(type => type.includes('directory'));
const shouldExist = types => types.some(type => type.includes('existing'));
function isDirectory(pathString) {
try {
return _fs.default.lstatSync(pathString).isDirectory();
} catch (e) {
if (e.code !== 'ENOENT') {
throw e;
}
return false;
}
}
/**
* Validates and normalizes config.
*
* @param {object} config
* @param {object} schema
* @param {string} rootDir
* @return {object}
*/
function sanitizeConfig(config, schema, rootDir) {
// Check for unknown fields
(0, _map.default)(config, (value, keyAny) => {
const key = keyAny;
if (!schema[key]) {
// Try to guess
const possibleOptions = Object.keys(schema);
const suggestedOption = possibleOptions.reduce((suggestion, option) => {
const steps = (0, _fastestLevenshtein.distance)(option, key);
if (steps < 2) {
return option;
}
return suggestion;
}, '');
throw new _error.default(`Unknown config option ${_kleur.default.bold(key)} was found, the value is:\n` + (0, _qI.stringify)(value) + (suggestedOption ? `\n\nDid you mean ${_kleur.default.bold(suggestedOption)}?` : ''), suggestedOption);
}
}); // Check all fields
const safeConfig = {};
(0, _map.default)(schema, (props, keyAny) => {
const key = keyAny;
let value = config[key]; // Custom processing
if (props.process) {
value = props.process(value, config, rootDir);
}
if (value === undefined) {
// Default value
value = props.default; // Check if the field is required
const isRequired = (0, _isFunction.default)(props.required) ? props.required(config) : props.required;
if (isRequired) {
const message = (0, _isString.default)(isRequired) ? isRequired : `${_kleur.default.bold(key)} config option is required.`;
throw new _error.default(message, key);
}
} else if (props.deprecated) {
logger.warn(`${key} config option is deprecated. ${props.deprecated}`);
} else if (props.removed) {
throw new _error.default(`${_kleur.default.bold(key)} config option was removed. ${props.removed}`);
}
if (value !== undefined && props.type) {
const types = (0, _castArray.default)(props.type); // Check type
const hasRightType = types.some(type => {
if (!typeCheckers[type]) {
throw new _error.default(`Wrong type ${_kleur.default.bold(type)} specified for ${_kleur.default.bold(key)} in schema.`);
}
return typeCheckers[type](value);
});
if (!hasRightType) {
const exampleValue = props.example || props.default;
const example = {};
if (exampleValue) {
example[key] = exampleValue;
}
const exampleText = exampleValue ? `
Example:
${(0, _qI.stringify)(example)}` : '';
throw new _error.default(`${_kleur.default.bold(key)} config option should be ${typesList(types)}, received ${(0, _typeDetect.default)(value)}.\n${exampleText}`, key);
} // Absolutize paths
if ((0, _isString.default)(value) && (shouldBeFile(types) || shouldBeDirectory(types))) {
value = _path.default.resolve(rootDir, value); // Check for existence
if (shouldExist(types)) {
if (shouldBeFile(types) && !_fs.default.existsSync(value)) {
throw new _error.default(`A file specified in ${_kleur.default.bold(key)} config option does not exist:\n${value}`, key);
}
if (shouldBeDirectory(types) && !isDirectory(value)) {
throw new _error.default(`A directory specified in ${_kleur.default.bold(key)} config option does not exist:\n${value}`, key);
}
}
}
}
safeConfig[keyAny] = value;
});
return safeConfig;
}