197 lines
7.0 KiB
JavaScript
197 lines
7.0 KiB
JavaScript
import { isBigint, isDate, isInfinite, isMap, isNaNValue, isRegExp, isSet, isUndefined, isSymbol, isArray, isError, isTypedArray, isURL, } from './is.js';
|
|
import { findArr } from './util.js';
|
|
function simpleTransformation(isApplicable, annotation, transform, untransform) {
|
|
return {
|
|
isApplicable,
|
|
annotation,
|
|
transform,
|
|
untransform,
|
|
};
|
|
}
|
|
const simpleRules = [
|
|
simpleTransformation(isUndefined, 'undefined', () => null, () => undefined),
|
|
simpleTransformation(isBigint, 'bigint', v => v.toString(), v => {
|
|
if (typeof BigInt !== 'undefined') {
|
|
return BigInt(v);
|
|
}
|
|
console.error('Please add a BigInt polyfill.');
|
|
return v;
|
|
}),
|
|
simpleTransformation(isDate, 'Date', v => v.toISOString(), v => new Date(v)),
|
|
simpleTransformation(isError, 'Error', (v, superJson) => {
|
|
const baseError = {
|
|
name: v.name,
|
|
message: v.message,
|
|
};
|
|
superJson.allowedErrorProps.forEach(prop => {
|
|
baseError[prop] = v[prop];
|
|
});
|
|
return baseError;
|
|
}, (v, superJson) => {
|
|
const e = new Error(v.message);
|
|
e.name = v.name;
|
|
e.stack = v.stack;
|
|
superJson.allowedErrorProps.forEach(prop => {
|
|
e[prop] = v[prop];
|
|
});
|
|
return e;
|
|
}),
|
|
simpleTransformation(isRegExp, 'regexp', v => '' + v, regex => {
|
|
const body = regex.slice(1, regex.lastIndexOf('/'));
|
|
const flags = regex.slice(regex.lastIndexOf('/') + 1);
|
|
return new RegExp(body, flags);
|
|
}),
|
|
simpleTransformation(isSet, 'set',
|
|
// (sets only exist in es6+)
|
|
// eslint-disable-next-line es5/no-es6-methods
|
|
v => [...v.values()], v => new Set(v)),
|
|
simpleTransformation(isMap, 'map', v => [...v.entries()], v => new Map(v)),
|
|
simpleTransformation((v) => isNaNValue(v) || isInfinite(v), 'number', v => {
|
|
if (isNaNValue(v)) {
|
|
return 'NaN';
|
|
}
|
|
if (v > 0) {
|
|
return 'Infinity';
|
|
}
|
|
else {
|
|
return '-Infinity';
|
|
}
|
|
}, Number),
|
|
simpleTransformation((v) => v === 0 && 1 / v === -Infinity, 'number', () => {
|
|
return '-0';
|
|
}, Number),
|
|
simpleTransformation(isURL, 'URL', v => v.toString(), v => new URL(v)),
|
|
];
|
|
function compositeTransformation(isApplicable, annotation, transform, untransform) {
|
|
return {
|
|
isApplicable,
|
|
annotation,
|
|
transform,
|
|
untransform,
|
|
};
|
|
}
|
|
const symbolRule = compositeTransformation((s, superJson) => {
|
|
if (isSymbol(s)) {
|
|
const isRegistered = !!superJson.symbolRegistry.getIdentifier(s);
|
|
return isRegistered;
|
|
}
|
|
return false;
|
|
}, (s, superJson) => {
|
|
const identifier = superJson.symbolRegistry.getIdentifier(s);
|
|
return ['symbol', identifier];
|
|
}, v => v.description, (_, a, superJson) => {
|
|
const value = superJson.symbolRegistry.getValue(a[1]);
|
|
if (!value) {
|
|
throw new Error('Trying to deserialize unknown symbol');
|
|
}
|
|
return value;
|
|
});
|
|
const constructorToName = [
|
|
Int8Array,
|
|
Uint8Array,
|
|
Int16Array,
|
|
Uint16Array,
|
|
Int32Array,
|
|
Uint32Array,
|
|
Float32Array,
|
|
Float64Array,
|
|
Uint8ClampedArray,
|
|
].reduce((obj, ctor) => {
|
|
obj[ctor.name] = ctor;
|
|
return obj;
|
|
}, {});
|
|
const typedArrayRule = compositeTransformation(isTypedArray, v => ['typed-array', v.constructor.name], v => [...v], (v, a) => {
|
|
const ctor = constructorToName[a[1]];
|
|
if (!ctor) {
|
|
throw new Error('Trying to deserialize unknown typed array');
|
|
}
|
|
return new ctor(v);
|
|
});
|
|
export function isInstanceOfRegisteredClass(potentialClass, superJson) {
|
|
if (potentialClass?.constructor) {
|
|
const isRegistered = !!superJson.classRegistry.getIdentifier(potentialClass.constructor);
|
|
return isRegistered;
|
|
}
|
|
return false;
|
|
}
|
|
const classRule = compositeTransformation(isInstanceOfRegisteredClass, (clazz, superJson) => {
|
|
const identifier = superJson.classRegistry.getIdentifier(clazz.constructor);
|
|
return ['class', identifier];
|
|
}, (clazz, superJson) => {
|
|
const allowedProps = superJson.classRegistry.getAllowedProps(clazz.constructor);
|
|
if (!allowedProps) {
|
|
return { ...clazz };
|
|
}
|
|
const result = {};
|
|
allowedProps.forEach(prop => {
|
|
result[prop] = clazz[prop];
|
|
});
|
|
return result;
|
|
}, (v, a, superJson) => {
|
|
const clazz = superJson.classRegistry.getValue(a[1]);
|
|
if (!clazz) {
|
|
throw new Error(`Trying to deserialize unknown class '${a[1]}' - check https://github.com/blitz-js/superjson/issues/116#issuecomment-773996564`);
|
|
}
|
|
return Object.assign(Object.create(clazz.prototype), v);
|
|
});
|
|
const customRule = compositeTransformation((value, superJson) => {
|
|
return !!superJson.customTransformerRegistry.findApplicable(value);
|
|
}, (value, superJson) => {
|
|
const transformer = superJson.customTransformerRegistry.findApplicable(value);
|
|
return ['custom', transformer.name];
|
|
}, (value, superJson) => {
|
|
const transformer = superJson.customTransformerRegistry.findApplicable(value);
|
|
return transformer.serialize(value);
|
|
}, (v, a, superJson) => {
|
|
const transformer = superJson.customTransformerRegistry.findByName(a[1]);
|
|
if (!transformer) {
|
|
throw new Error('Trying to deserialize unknown custom value');
|
|
}
|
|
return transformer.deserialize(v);
|
|
});
|
|
const compositeRules = [classRule, symbolRule, customRule, typedArrayRule];
|
|
export const transformValue = (value, superJson) => {
|
|
const applicableCompositeRule = findArr(compositeRules, rule => rule.isApplicable(value, superJson));
|
|
if (applicableCompositeRule) {
|
|
return {
|
|
value: applicableCompositeRule.transform(value, superJson),
|
|
type: applicableCompositeRule.annotation(value, superJson),
|
|
};
|
|
}
|
|
const applicableSimpleRule = findArr(simpleRules, rule => rule.isApplicable(value, superJson));
|
|
if (applicableSimpleRule) {
|
|
return {
|
|
value: applicableSimpleRule.transform(value, superJson),
|
|
type: applicableSimpleRule.annotation,
|
|
};
|
|
}
|
|
return undefined;
|
|
};
|
|
const simpleRulesByAnnotation = {};
|
|
simpleRules.forEach(rule => {
|
|
simpleRulesByAnnotation[rule.annotation] = rule;
|
|
});
|
|
export const untransformValue = (json, type, superJson) => {
|
|
if (isArray(type)) {
|
|
switch (type[0]) {
|
|
case 'symbol':
|
|
return symbolRule.untransform(json, type, superJson);
|
|
case 'class':
|
|
return classRule.untransform(json, type, superJson);
|
|
case 'custom':
|
|
return customRule.untransform(json, type, superJson);
|
|
case 'typed-array':
|
|
return typedArrayRule.untransform(json, type, superJson);
|
|
default:
|
|
throw new Error('Unknown transformation: ' + type);
|
|
}
|
|
}
|
|
else {
|
|
const transformation = simpleRulesByAnnotation[type];
|
|
if (!transformation) {
|
|
throw new Error('Unknown transformation: ' + type);
|
|
}
|
|
return transformation.untransform(json, superJson);
|
|
}
|
|
};
|
|
//# sourceMappingURL=transformer.js.map
|