提交学习笔记专用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

191 lines
7.3 KiB

  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.walker = exports.generateReferentialEqualityAnnotations = exports.applyReferentialEqualityAnnotations = exports.applyValueAnnotations = void 0;
  4. const is_js_1 = require("./is.cjs");
  5. const pathstringifier_js_1 = require("./pathstringifier.cjs");
  6. const transformer_js_1 = require("./transformer.cjs");
  7. const util_js_1 = require("./util.cjs");
  8. const pathstringifier_js_2 = require("./pathstringifier.cjs");
  9. const accessDeep_js_1 = require("./accessDeep.cjs");
  10. const enableLegacyPaths = (version) => version < 1;
  11. function traverse(tree, walker, version, origin = []) {
  12. if (!tree) {
  13. return;
  14. }
  15. const legacyPaths = enableLegacyPaths(version);
  16. if (!is_js_1.isArray(tree)) {
  17. util_js_1.forEach(tree, (subtree, key) => traverse(subtree, walker, version, [
  18. ...origin,
  19. ...pathstringifier_js_2.parsePath(key, legacyPaths),
  20. ]));
  21. return;
  22. }
  23. const [nodeValue, children] = tree;
  24. if (children) {
  25. util_js_1.forEach(children, (child, key) => {
  26. traverse(child, walker, version, [
  27. ...origin,
  28. ...pathstringifier_js_2.parsePath(key, legacyPaths),
  29. ]);
  30. });
  31. }
  32. walker(nodeValue, origin);
  33. }
  34. function applyValueAnnotations(plain, annotations, version, superJson) {
  35. traverse(annotations, (type, path) => {
  36. plain = accessDeep_js_1.setDeep(plain, path, v => transformer_js_1.untransformValue(v, type, superJson));
  37. }, version);
  38. return plain;
  39. }
  40. exports.applyValueAnnotations = applyValueAnnotations;
  41. function applyReferentialEqualityAnnotations(plain, annotations, version) {
  42. const legacyPaths = enableLegacyPaths(version);
  43. function apply(identicalPaths, path) {
  44. const object = accessDeep_js_1.getDeep(plain, pathstringifier_js_2.parsePath(path, legacyPaths));
  45. identicalPaths
  46. .map(path => pathstringifier_js_2.parsePath(path, legacyPaths))
  47. .forEach(identicalObjectPath => {
  48. plain = accessDeep_js_1.setDeep(plain, identicalObjectPath, () => object);
  49. });
  50. }
  51. if (is_js_1.isArray(annotations)) {
  52. const [root, other] = annotations;
  53. root.forEach(identicalPath => {
  54. plain = accessDeep_js_1.setDeep(plain, pathstringifier_js_2.parsePath(identicalPath, legacyPaths), () => plain);
  55. });
  56. if (other) {
  57. util_js_1.forEach(other, apply);
  58. }
  59. }
  60. else {
  61. util_js_1.forEach(annotations, apply);
  62. }
  63. return plain;
  64. }
  65. exports.applyReferentialEqualityAnnotations = applyReferentialEqualityAnnotations;
  66. const isDeep = (object, superJson) => is_js_1.isPlainObject(object) ||
  67. is_js_1.isArray(object) ||
  68. is_js_1.isMap(object) ||
  69. is_js_1.isSet(object) ||
  70. is_js_1.isError(object) ||
  71. transformer_js_1.isInstanceOfRegisteredClass(object, superJson);
  72. function addIdentity(object, path, identities) {
  73. const existingSet = identities.get(object);
  74. if (existingSet) {
  75. existingSet.push(path);
  76. }
  77. else {
  78. identities.set(object, [path]);
  79. }
  80. }
  81. function generateReferentialEqualityAnnotations(identitites, dedupe) {
  82. const result = {};
  83. let rootEqualityPaths = undefined;
  84. identitites.forEach(paths => {
  85. if (paths.length <= 1) {
  86. return;
  87. }
  88. // if we're not deduping, all of these objects continue existing.
  89. // putting the shortest path first makes it easier to parse for humans
  90. // if we're deduping though, only the first entry will still exist, so we can't do this optimisation.
  91. if (!dedupe) {
  92. paths = paths
  93. .map(path => path.map(String))
  94. .sort((a, b) => a.length - b.length);
  95. }
  96. const [representativePath, ...identicalPaths] = paths;
  97. if (representativePath.length === 0) {
  98. rootEqualityPaths = identicalPaths.map(pathstringifier_js_1.stringifyPath);
  99. }
  100. else {
  101. result[pathstringifier_js_1.stringifyPath(representativePath)] = identicalPaths.map(pathstringifier_js_1.stringifyPath);
  102. }
  103. });
  104. if (rootEqualityPaths) {
  105. if (is_js_1.isEmptyObject(result)) {
  106. return [rootEqualityPaths];
  107. }
  108. else {
  109. return [rootEqualityPaths, result];
  110. }
  111. }
  112. else {
  113. return is_js_1.isEmptyObject(result) ? undefined : result;
  114. }
  115. }
  116. exports.generateReferentialEqualityAnnotations = generateReferentialEqualityAnnotations;
  117. const walker = (object, identities, superJson, dedupe, path = [], objectsInThisPath = [], seenObjects = new Map()) => {
  118. const primitive = is_js_1.isPrimitive(object);
  119. if (!primitive) {
  120. addIdentity(object, path, identities);
  121. const seen = seenObjects.get(object);
  122. if (seen) {
  123. // short-circuit result if we've seen this object before
  124. return dedupe
  125. ? {
  126. transformedValue: null,
  127. }
  128. : seen;
  129. }
  130. }
  131. if (!isDeep(object, superJson)) {
  132. const transformed = transformer_js_1.transformValue(object, superJson);
  133. const result = transformed
  134. ? {
  135. transformedValue: transformed.value,
  136. annotations: [transformed.type],
  137. }
  138. : {
  139. transformedValue: object,
  140. };
  141. if (!primitive) {
  142. seenObjects.set(object, result);
  143. }
  144. return result;
  145. }
  146. if (util_js_1.includes(objectsInThisPath, object)) {
  147. // prevent circular references
  148. return {
  149. transformedValue: null,
  150. };
  151. }
  152. const transformationResult = transformer_js_1.transformValue(object, superJson);
  153. const transformed = transformationResult?.value ?? object;
  154. const transformedValue = is_js_1.isArray(transformed) ? [] : {};
  155. const innerAnnotations = {};
  156. util_js_1.forEach(transformed, (value, index) => {
  157. if (index === '__proto__' ||
  158. index === 'constructor' ||
  159. index === 'prototype') {
  160. throw new Error(`Detected property ${index}. This is a prototype pollution risk, please remove it from your object.`);
  161. }
  162. const recursiveResult = exports.walker(value, identities, superJson, dedupe, [...path, index], [...objectsInThisPath, object], seenObjects);
  163. transformedValue[index] = recursiveResult.transformedValue;
  164. if (is_js_1.isArray(recursiveResult.annotations)) {
  165. innerAnnotations[pathstringifier_js_1.escapeKey(index)] = recursiveResult.annotations;
  166. }
  167. else if (is_js_1.isPlainObject(recursiveResult.annotations)) {
  168. util_js_1.forEach(recursiveResult.annotations, (tree, key) => {
  169. innerAnnotations[pathstringifier_js_1.escapeKey(index) + '.' + key] = tree;
  170. });
  171. }
  172. });
  173. const result = is_js_1.isEmptyObject(innerAnnotations)
  174. ? {
  175. transformedValue,
  176. annotations: !!transformationResult
  177. ? [transformationResult.type]
  178. : undefined,
  179. }
  180. : {
  181. transformedValue,
  182. annotations: !!transformationResult
  183. ? [transformationResult.type, innerAnnotations]
  184. : innerAnnotations,
  185. };
  186. if (!primitive) {
  187. seenObjects.set(object, result);
  188. }
  189. return result;
  190. };
  191. exports.walker = walker;
  192. //# sourceMappingURL=plainer.js.map