object-shape-tester
Version:
Test object properties and value types.
84 lines (83 loc) • 3.54 kB
JavaScript
import { check } from '@augment-vir/assert';
import { extractErrorMessage, mapObjectValues } from '@augment-vir/common';
import { DefaultValueConstructionError } from '../errors/default-value-construction.error.js';
import { expandIndexedKeysKeys } from '../verify-shape/verify-shape.js';
import { isCustomSpecifier } from './custom-specifier.js';
import { getShapeSpecifier, isAndShapeSpecifier, isClassShapeSpecifier, isEnumShapeSpecifier, isExactShapeSpecifier, isIndexedKeysSpecifier, isNumericRangeShapeSpecifier, isOptionalShapeSpecifier, isOrShapeSpecifier, isShapeDefinition, isTupleShapeSpecifier, isUnknownShapeSpecifier, } from './shape-specifiers.js';
/**
* Creates a default value for the given shape.
*
* @category Internal
*/
export function shapeToDefaultValue(shape) {
return innerShapeToDefaultValue(shape);
}
function innerShapeToDefaultValue(shape) {
const specifier = getShapeSpecifier(shape);
if (isCustomSpecifier(shape)) {
return shape.defaultValue;
}
else if (specifier) {
if (isTupleShapeSpecifier(specifier)) {
return specifier.parts.map((part) => innerShapeToDefaultValue(part));
}
else if (isOptionalShapeSpecifier(specifier)) {
return innerShapeToDefaultValue(specifier.parts[0]);
}
else if (isNumericRangeShapeSpecifier(specifier)) {
return specifier.parts[0];
}
else if (isClassShapeSpecifier(specifier)) {
const classConstructor = specifier.parts[0];
try {
return new classConstructor();
}
catch (caught) {
throw new DefaultValueConstructionError(`Failed to create default value for classShape for class '${classConstructor.name}': ${extractErrorMessage(caught)}`);
}
}
else if (isOrShapeSpecifier(specifier) || isExactShapeSpecifier(specifier)) {
return innerShapeToDefaultValue(specifier.parts[0]);
}
else if (isAndShapeSpecifier(specifier)) {
return specifier.parts.reduce((combined, part) => {
return Object.assign(combined, innerShapeToDefaultValue(part));
}, {});
}
else if (isEnumShapeSpecifier(specifier)) {
return specifier.parts[1] || Object.values(specifier.parts[0])[0];
}
else if (isIndexedKeysSpecifier(specifier)) {
const keys = expandIndexedKeysKeys(specifier);
if (!specifier.parts[0].required || check.isBoolean(keys)) {
return {};
}
return Object.fromEntries(keys.map((key) => [
key,
innerShapeToDefaultValue(specifier.parts[0].values),
]));
}
else if (isUnknownShapeSpecifier(specifier)) {
return specifier.parts[0] ?? {};
/* v8 ignore next 7: this is an edge case fallback */
}
else {
throw new DefaultValueConstructionError(`found specifier but it matches no expected specifiers: ${String(specifier.specifierType)}`);
}
}
if (isShapeDefinition(shape)) {
return shapeToDefaultValue(shape.shape);
}
else if (shape instanceof RegExp) {
return shape;
}
else if (check.isArray(shape)) {
return shape.map(innerShapeToDefaultValue);
}
else if (check.isObject(shape)) {
return mapObjectValues(shape, (key, value) => {
return shapeToDefaultValue(value);
});
}
return shape;
}