reach.js 1.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. 'use strict';
  2. const Assert = require('./assert');
  3. const internals = {};
  4. module.exports = function (obj, chain, options) {
  5. if (chain === false ||
  6. chain === null ||
  7. chain === undefined) {
  8. return obj;
  9. }
  10. options = options || {};
  11. if (typeof options === 'string') {
  12. options = { separator: options };
  13. }
  14. const isChainArray = Array.isArray(chain);
  15. Assert(!isChainArray || !options.separator, 'Separator option no valid for array-based chain');
  16. const path = isChainArray ? chain : chain.split(options.separator || '.');
  17. let ref = obj;
  18. for (let i = 0; i < path.length; ++i) {
  19. let key = path[i];
  20. const type = options.iterables && internals.iterables(ref);
  21. if (Array.isArray(ref) ||
  22. type === 'set') {
  23. const number = Number(key);
  24. if (Number.isInteger(number)) {
  25. key = number < 0 ? ref.length + number : number;
  26. }
  27. }
  28. if (!ref ||
  29. typeof ref === 'function' && options.functions === false || // Defaults to true
  30. !type && ref[key] === undefined) {
  31. Assert(!options.strict || i + 1 === path.length, 'Missing segment', key, 'in reach path ', chain);
  32. Assert(typeof ref === 'object' || options.functions === true || typeof ref !== 'function', 'Invalid segment', key, 'in reach path ', chain);
  33. ref = options.default;
  34. break;
  35. }
  36. if (!type) {
  37. ref = ref[key];
  38. }
  39. else if (type === 'set') {
  40. ref = [...ref][key];
  41. }
  42. else { // type === 'map'
  43. ref = ref.get(key);
  44. }
  45. }
  46. return ref;
  47. };
  48. internals.iterables = function (ref) {
  49. if (ref instanceof Set) {
  50. return 'set';
  51. }
  52. if (ref instanceof Map) {
  53. return 'map';
  54. }
  55. };