compile.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. 'use strict';
  2. var Document;
  3. var utils = require('../../utils');
  4. /*!
  5. * exports
  6. */
  7. exports.compile = compile;
  8. exports.defineKey = defineKey;
  9. /*!
  10. * Compiles schemas.
  11. */
  12. function compile(tree, proto, prefix, options) {
  13. Document = Document || require('../../document');
  14. var keys = Object.keys(tree);
  15. var i = keys.length;
  16. var len = keys.length;
  17. var limb;
  18. var key;
  19. if (options.retainKeyOrder) {
  20. for (i = 0; i < len; ++i) {
  21. key = keys[i];
  22. limb = tree[key];
  23. defineKey(key,
  24. ((utils.getFunctionName(limb.constructor) === 'Object'
  25. && Object.keys(limb).length)
  26. && (!limb[options.typeKey] || (options.typeKey === 'type' && limb.type.type))
  27. ? limb
  28. : null)
  29. , proto
  30. , prefix
  31. , keys
  32. , options);
  33. }
  34. } else {
  35. while (i--) {
  36. key = keys[i];
  37. limb = tree[key];
  38. defineKey(key,
  39. ((utils.getFunctionName(limb.constructor) === 'Object'
  40. && Object.keys(limb).length)
  41. && (!limb[options.typeKey] || (options.typeKey === 'type' && limb.type.type))
  42. ? limb
  43. : null)
  44. , proto
  45. , prefix
  46. , keys
  47. , options);
  48. }
  49. }
  50. }
  51. /*!
  52. * Defines the accessor named prop on the incoming prototype.
  53. */
  54. function defineKey(prop, subprops, prototype, prefix, keys, options) {
  55. Document = Document || require('../../document');
  56. var path = (prefix ? prefix + '.' : '') + prop;
  57. prefix = prefix || '';
  58. if (subprops) {
  59. Object.defineProperty(prototype, prop, {
  60. enumerable: true,
  61. configurable: true,
  62. get: function() {
  63. var _this = this;
  64. if (!this.$__.getters) {
  65. this.$__.getters = {};
  66. }
  67. if (!this.$__.getters[path]) {
  68. var nested = Object.create(Document.prototype, getOwnPropertyDescriptors(this));
  69. // save scope for nested getters/setters
  70. if (!prefix) {
  71. nested.$__.scope = this;
  72. }
  73. nested.$__.nestedPath = path;
  74. Object.defineProperty(nested, 'schema', {
  75. enumerable: false,
  76. configurable: true,
  77. writable: false,
  78. value: prototype.schema
  79. });
  80. Object.defineProperty(nested, 'toObject', {
  81. enumerable: false,
  82. configurable: true,
  83. writable: false,
  84. value: function() {
  85. return utils.clone(_this.get(path), { retainKeyOrder: true });
  86. }
  87. });
  88. Object.defineProperty(nested, 'toJSON', {
  89. enumerable: false,
  90. configurable: true,
  91. writable: false,
  92. value: function() {
  93. return _this.get(path);
  94. }
  95. });
  96. Object.defineProperty(nested, '$__isNested', {
  97. enumerable: false,
  98. configurable: true,
  99. writable: false,
  100. value: true
  101. });
  102. compile(subprops, nested, path, options);
  103. this.$__.getters[path] = nested;
  104. }
  105. return this.$__.getters[path];
  106. },
  107. set: function(v) {
  108. if (v instanceof Document) {
  109. v = v.toObject({ transform: false });
  110. }
  111. var doc = this.$__.scope || this;
  112. return doc.$set(path, v);
  113. }
  114. });
  115. } else {
  116. Object.defineProperty(prototype, prop, {
  117. enumerable: true,
  118. configurable: true,
  119. get: function() {
  120. return this.get.call(this.$__.scope || this, path);
  121. },
  122. set: function(v) {
  123. return this.$set.call(this.$__.scope || this, path, v);
  124. }
  125. });
  126. }
  127. }
  128. // gets descriptors for all properties of `object`
  129. // makes all properties non-enumerable to match previous behavior to #2211
  130. function getOwnPropertyDescriptors(object) {
  131. var result = {};
  132. Object.getOwnPropertyNames(object).forEach(function(key) {
  133. result[key] = Object.getOwnPropertyDescriptor(object, key);
  134. // Assume these are schema paths, ignore them re: #5470
  135. if (result[key].get) {
  136. delete result[key];
  137. return;
  138. }
  139. result[key].enumerable = ['isNew', '$__', 'errors', '_doc'].indexOf(key) === -1;
  140. });
  141. return result;
  142. }