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.

path.js 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. "use strict";;
  2. var __importDefault = (this && this.__importDefault) || function (mod) {
  3. return (mod && mod.__esModule) ? mod : { "default": mod };
  4. };
  5. Object.defineProperty(exports, "__esModule", { value: true });
  6. var types_1 = __importDefault(require("./types"));
  7. var Op = Object.prototype;
  8. var hasOwn = Op.hasOwnProperty;
  9. function pathPlugin(fork) {
  10. var types = fork.use(types_1.default);
  11. var isArray = types.builtInTypes.array;
  12. var isNumber = types.builtInTypes.number;
  13. var Path = function Path(value, parentPath, name) {
  14. if (!(this instanceof Path)) {
  15. throw new Error("Path constructor cannot be invoked without 'new'");
  16. }
  17. if (parentPath) {
  18. if (!(parentPath instanceof Path)) {
  19. throw new Error("");
  20. }
  21. }
  22. else {
  23. parentPath = null;
  24. name = null;
  25. }
  26. // The value encapsulated by this Path, generally equal to
  27. // parentPath.value[name] if we have a parentPath.
  28. this.value = value;
  29. // The immediate parent Path of this Path.
  30. this.parentPath = parentPath;
  31. // The name of the property of parentPath.value through which this
  32. // Path's value was reached.
  33. this.name = name;
  34. // Calling path.get("child") multiple times always returns the same
  35. // child Path object, for both performance and consistency reasons.
  36. this.__childCache = null;
  37. };
  38. var Pp = Path.prototype;
  39. function getChildCache(path) {
  40. // Lazily create the child cache. This also cheapens cache
  41. // invalidation, since you can just reset path.__childCache to null.
  42. return path.__childCache || (path.__childCache = Object.create(null));
  43. }
  44. function getChildPath(path, name) {
  45. var cache = getChildCache(path);
  46. var actualChildValue = path.getValueProperty(name);
  47. var childPath = cache[name];
  48. if (!hasOwn.call(cache, name) ||
  49. // Ensure consistency between cache and reality.
  50. childPath.value !== actualChildValue) {
  51. childPath = cache[name] = new path.constructor(actualChildValue, path, name);
  52. }
  53. return childPath;
  54. }
  55. // This method is designed to be overridden by subclasses that need to
  56. // handle missing properties, etc.
  57. Pp.getValueProperty = function getValueProperty(name) {
  58. return this.value[name];
  59. };
  60. Pp.get = function get() {
  61. var names = [];
  62. for (var _i = 0; _i < arguments.length; _i++) {
  63. names[_i] = arguments[_i];
  64. }
  65. var path = this;
  66. var count = names.length;
  67. for (var i = 0; i < count; ++i) {
  68. path = getChildPath(path, names[i]);
  69. }
  70. return path;
  71. };
  72. Pp.each = function each(callback, context) {
  73. var childPaths = [];
  74. var len = this.value.length;
  75. var i = 0;
  76. // Collect all the original child paths before invoking the callback.
  77. for (var i = 0; i < len; ++i) {
  78. if (hasOwn.call(this.value, i)) {
  79. childPaths[i] = this.get(i);
  80. }
  81. }
  82. // Invoke the callback on just the original child paths, regardless of
  83. // any modifications made to the array by the callback. I chose these
  84. // semantics over cleverly invoking the callback on new elements because
  85. // this way is much easier to reason about.
  86. context = context || this;
  87. for (i = 0; i < len; ++i) {
  88. if (hasOwn.call(childPaths, i)) {
  89. callback.call(context, childPaths[i]);
  90. }
  91. }
  92. };
  93. Pp.map = function map(callback, context) {
  94. var result = [];
  95. this.each(function (childPath) {
  96. result.push(callback.call(this, childPath));
  97. }, context);
  98. return result;
  99. };
  100. Pp.filter = function filter(callback, context) {
  101. var result = [];
  102. this.each(function (childPath) {
  103. if (callback.call(this, childPath)) {
  104. result.push(childPath);
  105. }
  106. }, context);
  107. return result;
  108. };
  109. function emptyMoves() { }
  110. function getMoves(path, offset, start, end) {
  111. isArray.assert(path.value);
  112. if (offset === 0) {
  113. return emptyMoves;
  114. }
  115. var length = path.value.length;
  116. if (length < 1) {
  117. return emptyMoves;
  118. }
  119. var argc = arguments.length;
  120. if (argc === 2) {
  121. start = 0;
  122. end = length;
  123. }
  124. else if (argc === 3) {
  125. start = Math.max(start, 0);
  126. end = length;
  127. }
  128. else {
  129. start = Math.max(start, 0);
  130. end = Math.min(end, length);
  131. }
  132. isNumber.assert(start);
  133. isNumber.assert(end);
  134. var moves = Object.create(null);
  135. var cache = getChildCache(path);
  136. for (var i = start; i < end; ++i) {
  137. if (hasOwn.call(path.value, i)) {
  138. var childPath = path.get(i);
  139. if (childPath.name !== i) {
  140. throw new Error("");
  141. }
  142. var newIndex = i + offset;
  143. childPath.name = newIndex;
  144. moves[newIndex] = childPath;
  145. delete cache[i];
  146. }
  147. }
  148. delete cache.length;
  149. return function () {
  150. for (var newIndex in moves) {
  151. var childPath = moves[newIndex];
  152. if (childPath.name !== +newIndex) {
  153. throw new Error("");
  154. }
  155. cache[newIndex] = childPath;
  156. path.value[newIndex] = childPath.value;
  157. }
  158. };
  159. }
  160. Pp.shift = function shift() {
  161. var move = getMoves(this, -1);
  162. var result = this.value.shift();
  163. move();
  164. return result;
  165. };
  166. Pp.unshift = function unshift() {
  167. var args = [];
  168. for (var _i = 0; _i < arguments.length; _i++) {
  169. args[_i] = arguments[_i];
  170. }
  171. var move = getMoves(this, args.length);
  172. var result = this.value.unshift.apply(this.value, args);
  173. move();
  174. return result;
  175. };
  176. Pp.push = function push() {
  177. var args = [];
  178. for (var _i = 0; _i < arguments.length; _i++) {
  179. args[_i] = arguments[_i];
  180. }
  181. isArray.assert(this.value);
  182. delete getChildCache(this).length;
  183. return this.value.push.apply(this.value, args);
  184. };
  185. Pp.pop = function pop() {
  186. isArray.assert(this.value);
  187. var cache = getChildCache(this);
  188. delete cache[this.value.length - 1];
  189. delete cache.length;
  190. return this.value.pop();
  191. };
  192. Pp.insertAt = function insertAt(index) {
  193. var argc = arguments.length;
  194. var move = getMoves(this, argc - 1, index);
  195. if (move === emptyMoves && argc <= 1) {
  196. return this;
  197. }
  198. index = Math.max(index, 0);
  199. for (var i = 1; i < argc; ++i) {
  200. this.value[index + i - 1] = arguments[i];
  201. }
  202. move();
  203. return this;
  204. };
  205. Pp.insertBefore = function insertBefore() {
  206. var args = [];
  207. for (var _i = 0; _i < arguments.length; _i++) {
  208. args[_i] = arguments[_i];
  209. }
  210. var pp = this.parentPath;
  211. var argc = args.length;
  212. var insertAtArgs = [this.name];
  213. for (var i = 0; i < argc; ++i) {
  214. insertAtArgs.push(args[i]);
  215. }
  216. return pp.insertAt.apply(pp, insertAtArgs);
  217. };
  218. Pp.insertAfter = function insertAfter() {
  219. var args = [];
  220. for (var _i = 0; _i < arguments.length; _i++) {
  221. args[_i] = arguments[_i];
  222. }
  223. var pp = this.parentPath;
  224. var argc = args.length;
  225. var insertAtArgs = [this.name + 1];
  226. for (var i = 0; i < argc; ++i) {
  227. insertAtArgs.push(args[i]);
  228. }
  229. return pp.insertAt.apply(pp, insertAtArgs);
  230. };
  231. function repairRelationshipWithParent(path) {
  232. if (!(path instanceof Path)) {
  233. throw new Error("");
  234. }
  235. var pp = path.parentPath;
  236. if (!pp) {
  237. // Orphan paths have no relationship to repair.
  238. return path;
  239. }
  240. var parentValue = pp.value;
  241. var parentCache = getChildCache(pp);
  242. // Make sure parentCache[path.name] is populated.
  243. if (parentValue[path.name] === path.value) {
  244. parentCache[path.name] = path;
  245. }
  246. else if (isArray.check(parentValue)) {
  247. // Something caused path.name to become out of date, so attempt to
  248. // recover by searching for path.value in parentValue.
  249. var i = parentValue.indexOf(path.value);
  250. if (i >= 0) {
  251. parentCache[path.name = i] = path;
  252. }
  253. }
  254. else {
  255. // If path.value disagrees with parentValue[path.name], and
  256. // path.name is not an array index, let path.value become the new
  257. // parentValue[path.name] and update parentCache accordingly.
  258. parentValue[path.name] = path.value;
  259. parentCache[path.name] = path;
  260. }
  261. if (parentValue[path.name] !== path.value) {
  262. throw new Error("");
  263. }
  264. if (path.parentPath.get(path.name) !== path) {
  265. throw new Error("");
  266. }
  267. return path;
  268. }
  269. Pp.replace = function replace(replacement) {
  270. var results = [];
  271. var parentValue = this.parentPath.value;
  272. var parentCache = getChildCache(this.parentPath);
  273. var count = arguments.length;
  274. repairRelationshipWithParent(this);
  275. if (isArray.check(parentValue)) {
  276. var originalLength = parentValue.length;
  277. var move = getMoves(this.parentPath, count - 1, this.name + 1);
  278. var spliceArgs = [this.name, 1];
  279. for (var i = 0; i < count; ++i) {
  280. spliceArgs.push(arguments[i]);
  281. }
  282. var splicedOut = parentValue.splice.apply(parentValue, spliceArgs);
  283. if (splicedOut[0] !== this.value) {
  284. throw new Error("");
  285. }
  286. if (parentValue.length !== (originalLength - 1 + count)) {
  287. throw new Error("");
  288. }
  289. move();
  290. if (count === 0) {
  291. delete this.value;
  292. delete parentCache[this.name];
  293. this.__childCache = null;
  294. }
  295. else {
  296. if (parentValue[this.name] !== replacement) {
  297. throw new Error("");
  298. }
  299. if (this.value !== replacement) {
  300. this.value = replacement;
  301. this.__childCache = null;
  302. }
  303. for (i = 0; i < count; ++i) {
  304. results.push(this.parentPath.get(this.name + i));
  305. }
  306. if (results[0] !== this) {
  307. throw new Error("");
  308. }
  309. }
  310. }
  311. else if (count === 1) {
  312. if (this.value !== replacement) {
  313. this.__childCache = null;
  314. }
  315. this.value = parentValue[this.name] = replacement;
  316. results.push(this);
  317. }
  318. else if (count === 0) {
  319. delete parentValue[this.name];
  320. delete this.value;
  321. this.__childCache = null;
  322. // Leave this path cached as parentCache[this.name], even though
  323. // it no longer has a value defined.
  324. }
  325. else {
  326. throw new Error("Could not replace path");
  327. }
  328. return results;
  329. };
  330. return Path;
  331. }
  332. exports.default = pathPlugin;
  333. module.exports = exports["default"];