Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | 3x 3x 3x 21x 21x 3x 65x 65x 372x 65x 372x 372x 372x 372x 372x 372x 33x 33x 33x 33x 66x 33x 339x 336x 336x 336x 336x 336x 336x 417x 417x 64x 121x 64x 353x 417x 417x 336x 310x 310x 256x 26x 2x 336x 65x 3x 32x | import { each, indexOf } from 'core/utils/arrays';
import { type, isArray, isUndefined, isEmptyObject, isPlainObject, isString } from 'core/utils/types';
import {
PlainObject,
OptionsTemplate,
OptionsTemplateTypes,
OptionsTemplateType,
OptionsValidated,
Func,
OptionsValidatedResult,
} from 'core/typings';
const { stringify } = JSON;
/**
* A prefix and suffix tuple which serves as recognition pattern for template types.
*/
const templateTypePrefixSuffix: readonly [string, string] = ['__TPL_', '_TYPE__'];
/**
* A object which serves as a mapping for "normal" types and template types.
* Key = normal type string
* value = template type string
*/
const optionsTemplateTypes: OptionsTemplateTypesDictionary = ['boolean', 'number', 'string', 'array', 'object', 'function', 'null'].reduce(
(result, item) => {
result[item] = templateTypePrefixSuffix[0] + item + templateTypePrefixSuffix[1];
return result;
},
{} as OptionsTemplateTypesDictionary,
);
/**
* Validates the given options object according to the given template object and returns a object which looks like:
* {
* foreign : a object which consists of properties which aren't defined inside the template. (foreign properties)
* validated : a object which consists only of valid properties. (property name is inside the template and value has a correct type)
* }
* @param options The options object which shall be validated.
* @param template The template according to which the options object shall be validated.
* @param optionsDiff When provided the returned validated object will only have properties which are different to this objects properties.
* Example (assume all properties are valid to the template):
* Options object : { a: 'a', b: 'b', c: 'c' }
* optionsDiff object : { a: 'a', b: 'b', c: undefined }
* Returned validated object : { c: 'c' }
* Because the value of the properties a and b didn't change, they aren't included in the returned object.
* Without the optionsDiff object the returned validated object would be: { a: 'a', b: 'b', c: 'c' }
* @param doWriteErrors True if errors shall be logged into the console, false otherwise.
* @param propPath The propertyPath which lead to this object. (used for error logging)
*/
const validateRecursive = function <T extends PlainObject>(
options: T,
template: OptionsTemplate<Required<T>>,
optionsDiff: OptionsValidated<T>,
doWriteErrors?: boolean,
propPath?: string,
): OptionsValidatedResult<T> {
const validatedOptions: OptionsValidated<T> = {};
const optionsCopy: T = { ...options };
const props = Object.keys(template).filter((prop) => options.hasOwnProperty(prop));
each(props, (prop: Extract<keyof T, string>) => {
const optionsDiffValue: any = isUndefined(optionsDiff[prop]) ? {} : optionsDiff[prop];
const optionsValue: any = options[prop];
const templateValue: PlainObject | string | OptionsTemplateTypes | Array<OptionsTemplateTypes> = template[prop];
const templateIsComplex = isPlainObject(templateValue);
const propPrefix = propPath ? `${propPath}.` : '';
// if the template has a object as value, it means that the options are complex (verschachtelt)
if (templateIsComplex && isPlainObject(optionsValue)) {
const validatedResult = validateRecursive(optionsValue, templateValue as PlainObject, optionsDiffValue, doWriteErrors, propPrefix + prop);
validatedOptions[prop] = validatedResult.validated;
optionsCopy[prop] = validatedResult.foreign as any;
each([optionsCopy, validatedOptions], (value) => {
if (isEmptyObject(value[prop])) {
delete value[prop];
}
});
} else if (!templateIsComplex) {
let isValid = false;
const errorEnumStrings: Array<string> = [];
const errorPossibleTypes: Array<string> = [];
const optionsValueType = type(optionsValue);
const templateValueArr: Array<string | OptionsTemplateTypes> = !isArray(templateValue)
? [templateValue as string | OptionsTemplateTypes]
: (templateValue as Array<OptionsTemplateTypes>);
each(templateValueArr, (currTemplateType) => {
// if currType value isn't inside possibleTemplateTypes we assume its a enum string value
const isEnumString = indexOf(Object.values(optionsTemplateTypes), currTemplateType) < 0;
if (isEnumString && isString(optionsValue)) {
// split it into a array which contains all possible values for example: ["yes", "no", "maybe"]
const enumStringSplit = currTemplateType.split(' ');
isValid = !!enumStringSplit.find((possibility) => possibility === optionsValue);
// build error message
errorEnumStrings.push(...enumStringSplit);
} else {
isValid = optionsTemplateTypes[optionsValueType] === currTemplateType;
}
// build error message
errorPossibleTypes.push(isEnumString ? optionsTemplateTypes.string : currTemplateType);
// continue if invalid, break if valid
return !isValid;
});
if (isValid) {
const doStringifyComparison = isArray(optionsValue) || isPlainObject(optionsValue);
if (doStringifyComparison ? stringify(optionsValue) !== stringify(optionsDiffValue) : optionsValue !== optionsDiffValue) {
validatedOptions[prop] = optionsValue;
}
} else if (doWriteErrors) {
console.warn(
`${
`The option "${propPrefix}${prop}" wasn't set, because it doesn't accept the type [ ${optionsValueType.toUpperCase()} ] with the value of "${optionsValue}".\r\n` +
`Accepted types are: [ ${errorPossibleTypes.join(', ').toUpperCase()} ].\r\n`
}${errorEnumStrings.length > 0 ? `\r\nValid strings are: [ ${errorEnumStrings.join(', ')} ].` : ''}`,
);
}
delete optionsCopy[prop];
}
});
return {
foreign: optionsCopy,
validated: validatedOptions,
};
};
/**
* Validates the given options object according to the given template object and returns a object which looks like:
* {
* foreign : a object which consists of properties which aren't defined inside the template. (foreign properties)
* validated : a object which consists only of valid properties. (property name is inside the template and value has a correct type)
* }
* @param options The options object which shall be validated.
* @param template The template according to which the options object shall be validated.
* @param optionsDiff When provided the returned validated object will only have properties which are different to this objects properties.
* Example (assume all properties are valid to the template):
* Options object : { a: 'a', b: 'b', c: 'c' }
* optionsDiff object : { a: 'a', b: 'b', c: undefined }
* Returned validated object : { c: 'c' }
* Because the value of the properties a and b didn't change, they aren't included in the returned object.
* Without the optionsDiff object the returned validated object would be: { a: 'a', b: 'b', c: 'c' }
* @param doWriteErrors True if errors shall be logged into the console, false otherwise.
*/
const validate = function <T extends PlainObject>(
options: T,
template: OptionsTemplate<Required<T>>,
optionsDiff?: OptionsValidated<T>,
doWriteErrors?: boolean,
): OptionsValidatedResult<T> {
/*
if (!isEmptyObject(foreign) && doWriteErrors)
console.warn(`The following options are discarded due to invalidity:\r\n ${window.JSON.stringify(foreign, null, 2)}`);
//add values, which aren't specified in the template, to the finished validated object to prevent them from being discarded
if (keepForeignProps) {
Object.assign(result.validated, foreign);
}
*/
return validateRecursive(options, template, optionsDiff || {}, doWriteErrors || false);
};
export { validate, optionsTemplateTypes };
type OptionsTemplateTypesDictionary = {
readonly boolean: OptionsTemplateType<boolean>;
readonly number: OptionsTemplateType<number>;
readonly string: OptionsTemplateType<string>;
readonly array: OptionsTemplateType<Array<any>>;
readonly object: OptionsTemplateType<object>; // eslint-disable-line @typescript-eslint/ban-types
readonly function: OptionsTemplateType<Func>;
readonly null: OptionsTemplateType<null>;
};
|