123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- 'use strict';
- const documentSchemaSymbol = require('../../helpers/symbols').documentSchemaSymbol;
- const internalToObjectOptions = require('../../options').internalToObjectOptions;
- const utils = require('../../utils');
- let Document;
- const getSymbol = require('../../helpers/symbols').getSymbol;
- const scopeSymbol = require('../../helpers/symbols').scopeSymbol;
- const isPOJO = utils.isPOJO;
- /*!
- * exports
- */
- exports.compile = compile;
- exports.defineKey = defineKey;
- /*!
- * Compiles schemas.
- */
- function compile(tree, proto, prefix, options) {
- Document = Document || require('../../document');
- const typeKey = options.typeKey;
- for (const key of Object.keys(tree)) {
- const limb = tree[key];
- const hasSubprops = isPOJO(limb) &&
- Object.keys(limb).length > 0 &&
- (!limb[typeKey] || (typeKey === 'type' && isPOJO(limb.type) && limb.type.type));
- const subprops = hasSubprops ? limb : null;
- defineKey({ prop: key, subprops: subprops, prototype: proto, prefix: prefix, options: options });
- }
- }
- /*!
- * Defines the accessor named prop on the incoming prototype.
- */
- function defineKey({ prop, subprops, prototype, prefix, 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: this &&
- this.schema &&
- this.schema.options &&
- this.schema.options.toObject &&
- this.schema.options.toObject.virtuals || null
- }));
- }
- });
- Object.defineProperty(nested, '$__get', {
- enumerable: false,
- configurable: true,
- writable: false,
- value: function() {
- return _this.get(path, null, {
- virtuals: this && this.schema && this.schema.options && this.schema.options.toObject && this.schema.options.toObject.virtuals || null
- });
- }
- });
- Object.defineProperty(nested, 'toJSON', {
- enumerable: false,
- configurable: true,
- writable: false,
- value: function() {
- return _this.get(path, null, {
- virtuals: this && this.schema && this.schema.options && this.schema.options.toJSON && 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',
- '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;
- }
|