transformer.js 7.0 KB

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