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
			| 
								 
											3 years ago
										 
									 | 
							
								'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;
							 | 
						||
| 
								 | 
							
								}
							 |