transformer.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. import { isBigint, isDate, isInfinite, isMap, isNaNValue, isRegExp, isSet, isUndefined, isSymbol, isArray, isError, isTypedArray, isURL, } from './is.js';
  2. import { findArr } from './util.js';
  3. function simpleTransformation(isApplicable, annotation, transform, untransform) {
  4. return {
  5. isApplicable,
  6. annotation,
  7. transform,
  8. untransform,
  9. };
  10. }
  11. const simpleRules = [
  12. simpleTransformation(isUndefined, 'undefined', () => null, () => undefined),
  13. simpleTransformation(isBigint, 'bigint', v => v.toString(), v => {
  14. if (typeof BigInt !== 'undefined') {
  15. return BigInt(v);
  16. }
  17. console.error('Please add a BigInt polyfill.');
  18. return v;
  19. }),
  20. simpleTransformation(isDate, 'Date', v => v.toISOString(), v => new Date(v)),
  21. simpleTransformation(isError, 'Error', (v, superJson) => {
  22. const baseError = {
  23. name: v.name,
  24. message: v.message,
  25. };
  26. if ('cause' in v) {
  27. baseError.cause = v.cause;
  28. }
  29. superJson.allowedErrorProps.forEach(prop => {
  30. baseError[prop] = v[prop];
  31. });
  32. return baseError;
  33. }, (v, superJson) => {
  34. const e = new Error(v.message, { cause: v.cause });
  35. e.name = v.name;
  36. e.stack = v.stack;
  37. superJson.allowedErrorProps.forEach(prop => {
  38. e[prop] = v[prop];
  39. });
  40. return e;
  41. }),
  42. simpleTransformation(isRegExp, 'regexp', v => '' + v, regex => {
  43. const body = regex.slice(1, regex.lastIndexOf('/'));
  44. const flags = regex.slice(regex.lastIndexOf('/') + 1);
  45. return new RegExp(body, flags);
  46. }),
  47. simpleTransformation(isSet, 'set',
  48. // (sets only exist in es6+)
  49. // eslint-disable-next-line es5/no-es6-methods
  50. v => [...v.values()], v => new Set(v)),
  51. simpleTransformation(isMap, 'map', v => [...v.entries()], v => new Map(v)),
  52. simpleTransformation((v) => isNaNValue(v) || isInfinite(v), 'number', v => {
  53. if (isNaNValue(v)) {
  54. return 'NaN';
  55. }
  56. if (v > 0) {
  57. return 'Infinity';
  58. }
  59. else {
  60. return '-Infinity';
  61. }
  62. }, Number),
  63. simpleTransformation((v) => v === 0 && 1 / v === -Infinity, 'number', () => {
  64. return '-0';
  65. }, Number),
  66. simpleTransformation(isURL, 'URL', v => v.toString(), v => new URL(v)),
  67. ];
  68. function compositeTransformation(isApplicable, annotation, transform, untransform) {
  69. return {
  70. isApplicable,
  71. annotation,
  72. transform,
  73. untransform,
  74. };
  75. }
  76. const symbolRule = compositeTransformation((s, superJson) => {
  77. if (isSymbol(s)) {
  78. const isRegistered = !!superJson.symbolRegistry.getIdentifier(s);
  79. return isRegistered;
  80. }
  81. return false;
  82. }, (s, superJson) => {
  83. const identifier = superJson.symbolRegistry.getIdentifier(s);
  84. return ['symbol', identifier];
  85. }, v => v.description, (_, a, superJson) => {
  86. const value = superJson.symbolRegistry.getValue(a[1]);
  87. if (!value) {
  88. throw new Error('Trying to deserialize unknown symbol');
  89. }
  90. return value;
  91. });
  92. const constructorToName = [
  93. Int8Array,
  94. Uint8Array,
  95. Int16Array,
  96. Uint16Array,
  97. Int32Array,
  98. Uint32Array,
  99. Float32Array,
  100. Float64Array,
  101. Uint8ClampedArray,
  102. ].reduce((obj, ctor) => {
  103. obj[ctor.name] = ctor;
  104. return obj;
  105. }, {});
  106. const typedArrayRule = compositeTransformation(isTypedArray, v => ['typed-array', v.constructor.name], v => [...v], (v, a) => {
  107. const ctor = constructorToName[a[1]];
  108. if (!ctor) {
  109. throw new Error('Trying to deserialize unknown typed array');
  110. }
  111. return new ctor(v);
  112. });
  113. export function isInstanceOfRegisteredClass(potentialClass, superJson) {
  114. if (potentialClass?.constructor) {
  115. const isRegistered = !!superJson.classRegistry.getIdentifier(potentialClass.constructor);
  116. return isRegistered;
  117. }
  118. return false;
  119. }
  120. const classRule = compositeTransformation(isInstanceOfRegisteredClass, (clazz, superJson) => {
  121. const identifier = superJson.classRegistry.getIdentifier(clazz.constructor);
  122. return ['class', identifier];
  123. }, (clazz, superJson) => {
  124. const allowedProps = superJson.classRegistry.getAllowedProps(clazz.constructor);
  125. if (!allowedProps) {
  126. return { ...clazz };
  127. }
  128. const result = {};
  129. allowedProps.forEach(prop => {
  130. result[prop] = clazz[prop];
  131. });
  132. return result;
  133. }, (v, a, superJson) => {
  134. const clazz = superJson.classRegistry.getValue(a[1]);
  135. if (!clazz) {
  136. throw new Error(`Trying to deserialize unknown class '${a[1]}' - check https://github.com/blitz-js/superjson/issues/116#issuecomment-773996564`);
  137. }
  138. return Object.assign(Object.create(clazz.prototype), v);
  139. });
  140. const customRule = compositeTransformation((value, superJson) => {
  141. return !!superJson.customTransformerRegistry.findApplicable(value);
  142. }, (value, superJson) => {
  143. const transformer = superJson.customTransformerRegistry.findApplicable(value);
  144. return ['custom', transformer.name];
  145. }, (value, superJson) => {
  146. const transformer = superJson.customTransformerRegistry.findApplicable(value);
  147. return transformer.serialize(value);
  148. }, (v, a, superJson) => {
  149. const transformer = superJson.customTransformerRegistry.findByName(a[1]);
  150. if (!transformer) {
  151. throw new Error('Trying to deserialize unknown custom value');
  152. }
  153. return transformer.deserialize(v);
  154. });
  155. const compositeRules = [classRule, symbolRule, customRule, typedArrayRule];
  156. export const transformValue = (value, superJson) => {
  157. const applicableCompositeRule = findArr(compositeRules, rule => rule.isApplicable(value, superJson));
  158. if (applicableCompositeRule) {
  159. return {
  160. value: applicableCompositeRule.transform(value, superJson),
  161. type: applicableCompositeRule.annotation(value, superJson),
  162. };
  163. }
  164. const applicableSimpleRule = findArr(simpleRules, rule => rule.isApplicable(value, superJson));
  165. if (applicableSimpleRule) {
  166. return {
  167. value: applicableSimpleRule.transform(value, superJson),
  168. type: applicableSimpleRule.annotation,
  169. };
  170. }
  171. return undefined;
  172. };
  173. const simpleRulesByAnnotation = {};
  174. simpleRules.forEach(rule => {
  175. simpleRulesByAnnotation[rule.annotation] = rule;
  176. });
  177. export const untransformValue = (json, type, superJson) => {
  178. if (isArray(type)) {
  179. switch (type[0]) {
  180. case 'symbol':
  181. return symbolRule.untransform(json, type, superJson);
  182. case 'class':
  183. return classRule.untransform(json, type, superJson);
  184. case 'custom':
  185. return customRule.untransform(json, type, superJson);
  186. case 'typed-array':
  187. return typedArrayRule.untransform(json, type, superJson);
  188. default:
  189. throw new Error('Unknown transformation: ' + type);
  190. }
  191. }
  192. else {
  193. const transformation = simpleRulesByAnnotation[type];
  194. if (!transformation) {
  195. throw new Error('Unknown transformation: ' + type);
  196. }
  197. return transformation.untransform(json, superJson);
  198. }
  199. };
  200. //# sourceMappingURL=transformer.js.map