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;
 | |
| }
 |