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.
		
		
		
		
		
			
		
			
				
					
					
						
							212 lines
						
					
					
						
							5.8 KiB
						
					
					
				
			
		
		
	
	
							212 lines
						
					
					
						
							5.8 KiB
						
					
					
				'use strict';
 | 
						|
 | 
						|
const documentSchemaSymbol = require('../../helpers/symbols').documentSchemaSymbol;
 | 
						|
const get = require('../../helpers/get');
 | 
						|
const internalToObjectOptions = require('../../options').internalToObjectOptions;
 | 
						|
const utils = require('../../utils');
 | 
						|
 | 
						|
let Document;
 | 
						|
const getSymbol = require('../../helpers/symbols').getSymbol;
 | 
						|
const scopeSymbol = require('../../helpers/symbols').scopeSymbol;
 | 
						|
 | 
						|
/*!
 | 
						|
 * exports
 | 
						|
 */
 | 
						|
 | 
						|
exports.compile = compile;
 | 
						|
exports.defineKey = defineKey;
 | 
						|
 | 
						|
/*!
 | 
						|
 * Compiles schemas.
 | 
						|
 */
 | 
						|
 | 
						|
function compile(tree, proto, prefix, options) {
 | 
						|
  Document = Document || require('../../document');
 | 
						|
  const keys = Object.keys(tree);
 | 
						|
  const len = keys.length;
 | 
						|
  let limb;
 | 
						|
  let key;
 | 
						|
 | 
						|
  for (let i = 0; i < len; ++i) {
 | 
						|
    key = keys[i];
 | 
						|
    limb = tree[key];
 | 
						|
 | 
						|
    const hasSubprops = utils.isPOJO(limb) && Object.keys(limb).length &&
 | 
						|
      (!limb[options.typeKey] || (options.typeKey === 'type' && limb.type.type));
 | 
						|
    const subprops = hasSubprops ? limb : null;
 | 
						|
 | 
						|
    defineKey(key, subprops, proto, prefix, keys, options);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/*!
 | 
						|
 * Defines the accessor named prop on the incoming prototype.
 | 
						|
 */
 | 
						|
 | 
						|
function defineKey(prop, subprops, prototype, prefix, keys, options) {
 | 
						|
  Document = Document || require('../../document');
 | 
						|
  const path = (prefix ? prefix + '.' : '') + prop;
 | 
						|
  prefix = prefix || '';
 | 
						|
 | 
						|
  if (subprops) {
 | 
						|
    Object.defineProperty(prototype, prop, {
 | 
						|
      enumerable: true,
 | 
						|
      configurable: true,
 | 
						|
      get: function() {
 | 
						|
        const _this = this;
 | 
						|
        if (!this.$__.getters) {
 | 
						|
          this.$__.getters = {};
 | 
						|
        }
 | 
						|
 | 
						|
        if (!this.$__.getters[path]) {
 | 
						|
          const nested = Object.create(Document.prototype, getOwnPropertyDescriptors(this));
 | 
						|
 | 
						|
          // save scope for nested getters/setters
 | 
						|
          if (!prefix) {
 | 
						|
            nested.$__[scopeSymbol] = this;
 | 
						|
          }
 | 
						|
          nested.$__.nestedPath = path;
 | 
						|
 | 
						|
          Object.defineProperty(nested, 'schema', {
 | 
						|
            enumerable: false,
 | 
						|
            configurable: true,
 | 
						|
            writable: false,
 | 
						|
            value: prototype.schema
 | 
						|
          });
 | 
						|
 | 
						|
          Object.defineProperty(nested, '$__schema', {
 | 
						|
            enumerable: false,
 | 
						|
            configurable: true,
 | 
						|
            writable: false,
 | 
						|
            value: prototype.schema
 | 
						|
          });
 | 
						|
 | 
						|
          Object.defineProperty(nested, documentSchemaSymbol, {
 | 
						|
            enumerable: false,
 | 
						|
            configurable: true,
 | 
						|
            writable: false,
 | 
						|
            value: prototype.schema
 | 
						|
          });
 | 
						|
 | 
						|
          Object.defineProperty(nested, 'toObject', {
 | 
						|
            enumerable: false,
 | 
						|
            configurable: true,
 | 
						|
            writable: false,
 | 
						|
            value: function() {
 | 
						|
              return utils.clone(_this.get(path, null, {
 | 
						|
                virtuals: get(this, 'schema.options.toObject.virtuals', null)
 | 
						|
              }));
 | 
						|
            }
 | 
						|
          });
 | 
						|
 | 
						|
          Object.defineProperty(nested, '$__get', {
 | 
						|
            enumerable: false,
 | 
						|
            configurable: true,
 | 
						|
            writable: false,
 | 
						|
            value: function() {
 | 
						|
              return _this.get(path, null, {
 | 
						|
                virtuals: get(this, 'schema.options.toObject.virtuals', null)
 | 
						|
              });
 | 
						|
            }
 | 
						|
          });
 | 
						|
 | 
						|
          Object.defineProperty(nested, 'toJSON', {
 | 
						|
            enumerable: false,
 | 
						|
            configurable: true,
 | 
						|
            writable: false,
 | 
						|
            value: function() {
 | 
						|
              return _this.get(path, null, {
 | 
						|
                virtuals: get(_this, 'schema.options.toJSON.virtuals', null)
 | 
						|
              });
 | 
						|
            }
 | 
						|
          });
 | 
						|
 | 
						|
          Object.defineProperty(nested, '$__isNested', {
 | 
						|
            enumerable: false,
 | 
						|
            configurable: true,
 | 
						|
            writable: false,
 | 
						|
            value: true
 | 
						|
          });
 | 
						|
 | 
						|
          const _isEmptyOptions = Object.freeze({
 | 
						|
            minimize: true,
 | 
						|
            virtuals: false,
 | 
						|
            getters: false,
 | 
						|
            transform: false
 | 
						|
          });
 | 
						|
          Object.defineProperty(nested, '$isEmpty', {
 | 
						|
            enumerable: false,
 | 
						|
            configurable: true,
 | 
						|
            writable: false,
 | 
						|
            value: function() {
 | 
						|
              return Object.keys(this.get(path, null, _isEmptyOptions) || {}).length === 0;
 | 
						|
            }
 | 
						|
          });
 | 
						|
 | 
						|
          Object.defineProperty(nested, '$__parent', {
 | 
						|
            enumerable: false,
 | 
						|
            configurable: true,
 | 
						|
            writable: false,
 | 
						|
            value: this
 | 
						|
          });
 | 
						|
 | 
						|
          compile(subprops, nested, path, options);
 | 
						|
          this.$__.getters[path] = nested;
 | 
						|
        }
 | 
						|
 | 
						|
        return this.$__.getters[path];
 | 
						|
      },
 | 
						|
      set: function(v) {
 | 
						|
        if (v != null && v.$__isNested) {
 | 
						|
          // Convert top-level to POJO, but leave subdocs hydrated so `$set`
 | 
						|
          // can handle them. See gh-9293.
 | 
						|
          v = v.$__get();
 | 
						|
        } else if (v instanceof Document && !v.$__isNested) {
 | 
						|
          v = v.toObject(internalToObjectOptions);
 | 
						|
        }
 | 
						|
        const doc = this.$__[scopeSymbol] || this;
 | 
						|
        doc.$set(path, v);
 | 
						|
      }
 | 
						|
    });
 | 
						|
  } else {
 | 
						|
    Object.defineProperty(prototype, prop, {
 | 
						|
      enumerable: true,
 | 
						|
      configurable: true,
 | 
						|
      get: function() {
 | 
						|
        return this[getSymbol].call(this.$__[scopeSymbol] || this, path);
 | 
						|
      },
 | 
						|
      set: function(v) {
 | 
						|
        this.$set.call(this.$__[scopeSymbol] || this, path, v);
 | 
						|
      }
 | 
						|
    });
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
// gets descriptors for all properties of `object`
 | 
						|
// makes all properties non-enumerable to match previous behavior to #2211
 | 
						|
function getOwnPropertyDescriptors(object) {
 | 
						|
  const result = {};
 | 
						|
 | 
						|
  Object.getOwnPropertyNames(object).forEach(function(key) {
 | 
						|
    const skip = [
 | 
						|
      'isNew',
 | 
						|
      '$__',
 | 
						|
      'errors',
 | 
						|
      '_doc',
 | 
						|
      '$locals',
 | 
						|
      '$op',
 | 
						|
      '__parentArray',
 | 
						|
      '__index',
 | 
						|
      '$isDocumentArrayElement'
 | 
						|
    ].indexOf(key) === -1;
 | 
						|
    if (skip) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    result[key] = Object.getOwnPropertyDescriptor(object, key);
 | 
						|
    result[key].enumerable = false;
 | 
						|
  });
 | 
						|
 | 
						|
  return result;
 | 
						|
}
 |