mongoose 5.7.7 → 5.7.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/History.md CHANGED
@@ -1,3 +1,14 @@
1
+ 5.7.8 / 2019-11-04
2
+ ==================
3
+ * fix(document): allow manually populating path within document array #8273
4
+ * fix(populate): update top-level `populated()` when updating document array with populated subpaths #8265
5
+ * fix(cursor): throw error when using aggregation cursor as async iterator #8280
6
+ * fix(schema): retain `_id: false` in schema after nesting in another schema #8274
7
+ * fix(document): make Document class an event emitter to support defining documents without models in node #8272
8
+ * docs: document return types for `.discriminator()` #8287
9
+ * docs(connection): add note about exporting schemas, not models, in multi connection paradigm #8275
10
+ * docs: clarify that transforms defined in `toObject()` options are applied to subdocs #8260
11
+
1
12
  5.7.7 / 2019-10-24
2
13
  ==================
3
14
  * fix(populate): make populate virtual consistently an empty array if local field is only empty arrays #8230
package/lib/aggregate.js CHANGED
@@ -1011,20 +1011,20 @@ Aggregate.prototype.catch = function(reject) {
1011
1011
 
1012
1012
  /**
1013
1013
  * Returns an asyncIterator for use with [`for/await/of` loops](http://bit.ly/async-iterators)
1014
- * This function *only* works for `find()` queries.
1015
1014
  * You do not need to call this function explicitly, the JavaScript runtime
1016
1015
  * will call it for you.
1017
1016
  *
1018
1017
  * ####Example
1019
1018
  *
1020
- * for await (const doc of Model.find().sort({ name: 1 })) {
1019
+ * const agg = Model.aggregate([{ $match: { age: { $gte: 25 } } }]);
1020
+ * for await (const doc of agg) {
1021
1021
  * console.log(doc.name);
1022
1022
  * }
1023
1023
  *
1024
1024
  * Node.js 10.x supports async iterators natively without any flags. You can
1025
1025
  * enable async iterators in Node.js 8.x using the [`--harmony_async_iteration` flag](https://github.com/tc39/proposal-async-iteration/issues/117#issuecomment-346695187).
1026
1026
  *
1027
- * **Note:** This function is not if `Symbol.asyncIterator` is undefined. If
1027
+ * **Note:** This function is not set if `Symbol.asyncIterator` is undefined. If
1028
1028
  * `Symbol.asyncIterator` is undefined, that means your Node.js version does not
1029
1029
  * support async iterators.
1030
1030
  *
@@ -4,6 +4,7 @@
4
4
 
5
5
  'use strict';
6
6
 
7
+ const MongooseError = require('../error/mongooseError');
7
8
  const Readable = require('stream').Readable;
8
9
  const eachAsync = require('../helpers/cursor/eachAsync');
9
10
  const util = require('util');
@@ -94,6 +95,15 @@ AggregationCursor.prototype._read = function() {
94
95
  });
95
96
  };
96
97
 
98
+ if (Symbol.asyncIterator != null) {
99
+ const msg = 'Mongoose does not support using async iterators with an ' +
100
+ 'existing aggregation cursor. See http://bit.ly/mongoose-async-iterate-aggregation';
101
+
102
+ AggregationCursor.prototype[Symbol.asyncIterator] = function() {
103
+ throw new MongooseError(msg);
104
+ };
105
+ }
106
+
97
107
  /**
98
108
  * Registers a transform function which subsequently maps documents retrieved
99
109
  * via the streams interface or `.next()`
package/lib/document.js CHANGED
@@ -184,6 +184,10 @@ utils.each(
184
184
 
185
185
  Document.prototype.constructor = Document;
186
186
 
187
+ for (const i in EventEmitter.prototype) {
188
+ Document[i] = EventEmitter.prototype[i];
189
+ }
190
+
187
191
  /**
188
192
  * The documents schema.
189
193
  *
@@ -1163,6 +1167,21 @@ Document.prototype.$set = function $set(path, val, type, options) {
1163
1167
  val = schema.applySetters(val, this, false, priorVal);
1164
1168
  }
1165
1169
 
1170
+ if (schema.$isMongooseDocumentArray &&
1171
+ Array.isArray(val) &&
1172
+ val.length > 0 &&
1173
+ val[0] != null &&
1174
+ val[0].$__ != null &&
1175
+ val[0].$__.populated != null) {
1176
+ const populatedPaths = Object.keys(val[0].$__.populated);
1177
+ for (const populatedPath of populatedPaths) {
1178
+ this.populated(path + '.' + populatedPath,
1179
+ val.map(v => v.populated(populatedPath)),
1180
+ val[0].$__.populated[populatedPath].options);
1181
+ }
1182
+ didPopulate = true;
1183
+ }
1184
+
1166
1185
  if (!didPopulate && this.$__.populated) {
1167
1186
  delete this.$__.populated[path];
1168
1187
  }
@@ -2970,7 +2989,6 @@ Document.prototype.$toObject = function(options, json) {
2970
2989
  *
2971
2990
  * If you want to skip transformations, use `transform: false`:
2972
2991
  *
2973
- * if (!schema.options.toObject) schema.options.toObject = {};
2974
2992
  * schema.options.toObject.hide = '_id';
2975
2993
  * schema.options.toObject.transform = function (doc, ret, options) {
2976
2994
  * if (options.hide) {
@@ -2986,7 +3004,26 @@ Document.prototype.$toObject = function(options, json) {
2986
3004
  * doc.toObject({ hide: 'secret _id', transform: false });// { _id: 'anId', secret: 47, name: 'Wreck-it Ralph' }
2987
3005
  * doc.toObject({ hide: 'secret _id', transform: true }); // { name: 'Wreck-it Ralph' }
2988
3006
  *
2989
- * Transforms are applied _only to the document and are not applied to sub-documents_.
3007
+ * If you pass a transform in `toObject()` options, Mongoose will apply the transform
3008
+ * to [subdocuments](/docs/subdocs.html) in addition to the top-level document.
3009
+ * Similarly, `transform: false` skips transforms for all subdocuments.
3010
+ * Note that this is behavior is different for transforms defined in the schema:
3011
+ * if you define a transform in `schema.options.toObject.transform`, that transform
3012
+ * will **not** apply to subdocuments.
3013
+ *
3014
+ * const memberSchema = new Schema({ name: String, email: String });
3015
+ * const groupSchema = new Schema({ members: [memberSchema], name: String, email });
3016
+ * const Group = mongoose.model('Group', groupSchema);
3017
+ *
3018
+ * const doc = new Group({
3019
+ * name: 'Engineering',
3020
+ * email: 'dev@mongoosejs.io',
3021
+ * members: [{ name: 'Val', email: 'val@mongoosejs.io' }]
3022
+ * });
3023
+ *
3024
+ * // Removes `email` from both top-level document **and** array elements
3025
+ * // { name: 'Engineering', members: [{ name: 'Val' }] }
3026
+ * doc.toObject({ transform: (doc, ret) => { delete ret.email; return ret; } });
2990
3027
  *
2991
3028
  * Transforms, like all of these options, are also available for `toJSON`. See [this guide to `JSON.stringify()`](https://thecodebarbarian.com/the-80-20-guide-to-json-stringify-in-javascript.html) to learn why `toJSON()` and `toObject()` are separate functions.
2992
3029
  *
package/lib/model.js CHANGED
@@ -1067,6 +1067,7 @@ Model.exists = function exists(filter, options, callback) {
1067
1067
  * @param {String} name discriminator model name
1068
1068
  * @param {Schema} schema discriminator model schema
1069
1069
  * @param {String} [value] the string stored in the `discriminatorKey` property. If not specified, Mongoose uses the `name` parameter.
1070
+ * @return {Model} The newly created discriminator model
1070
1071
  * @api public
1071
1072
  */
1072
1073
 
@@ -1149,7 +1150,7 @@ for (const i in EventEmitter.prototype) {
1149
1150
  *
1150
1151
  * Mongoose calls this function automatically when a model is created using
1151
1152
  * [`mongoose.model()`](/docs/api.html#mongoose_Mongoose-model) or
1152
- * * [`connection.model()`](/docs/api.html#connection_Connection-model), so you
1153
+ * [`connection.model()`](/docs/api.html#connection_Connection-model), so you
1153
1154
  * don't need to call it. This function is also idempotent, so you may call it
1154
1155
  * to get back a promise that will resolve when your indexes are finished
1155
1156
  * building as an alternative to [`MyModel.on('index')`](/docs/guide.html#indexes)
@@ -3411,7 +3412,7 @@ Model.bulkWrite = function(ops, options, callback) {
3411
3412
  * var mongooseCandy = Candy.hydrate({ _id: '54108337212ffb6d459f854c', type: 'jelly bean' });
3412
3413
  *
3413
3414
  * @param {Object} obj
3414
- * @return {Model} document instance
3415
+ * @return {Document} document instance
3415
3416
  * @api public
3416
3417
  */
3417
3418
 
@@ -107,7 +107,8 @@ Object.defineProperty(SchemaTypeOptions.prototype, 'ref', opts);
107
107
  Object.defineProperty(SchemaTypeOptions.prototype, 'select', opts);
108
108
 
109
109
  /**
110
- * If truthy, Mongoose will build an index on this path when the model is
110
+ * If [truthy](https://masteringjs.io/tutorials/fundamentals/truthy), Mongoose will
111
+ * build an index on this path when the model is
111
112
  * compiled.
112
113
  *
113
114
  * @api public
@@ -120,7 +121,8 @@ Object.defineProperty(SchemaTypeOptions.prototype, 'select', opts);
120
121
  Object.defineProperty(SchemaTypeOptions.prototype, 'index', opts);
121
122
 
122
123
  /**
123
- * If truthy, Mongoose will build a unique index on this path when the
124
+ * If [truthy](https://masteringjs.io/tutorials/fundamentals/truthy), Mongoose
125
+ * will build a unique index on this path when the
124
126
  * model is compiled. [The `unique` option is **not** a validator](/docs/validation.html#the-unique-option-is-not-a-validator).
125
127
  *
126
128
  * @api public
@@ -133,7 +135,8 @@ Object.defineProperty(SchemaTypeOptions.prototype, 'index', opts);
133
135
  Object.defineProperty(SchemaTypeOptions.prototype, 'unique', opts);
134
136
 
135
137
  /**
136
- * If truthy, Mongoose will disallow changes to this path once the document
138
+ * If [truthy](https://masteringjs.io/tutorials/fundamentals/truthy), Mongoose will
139
+ * disallow changes to this path once the document
137
140
  * is saved to the database for the first time. Read more about [immutability in Mongoose here](http://thecodebarbarian.com/whats-new-in-mongoose-5-6-immutable-properties.html).
138
141
  *
139
142
  * @api public
@@ -146,7 +149,8 @@ Object.defineProperty(SchemaTypeOptions.prototype, 'unique', opts);
146
149
  Object.defineProperty(SchemaTypeOptions.prototype, 'immutable', opts);
147
150
 
148
151
  /**
149
- * If truthy, Mongoose will build a sparse index on this path.
152
+ * If [truthy](https://masteringjs.io/tutorials/fundamentals/truthy), Mongoose will
153
+ * build a sparse index on this path.
150
154
  *
151
155
  * @api public
152
156
  * @property sparse
@@ -158,7 +162,8 @@ Object.defineProperty(SchemaTypeOptions.prototype, 'immutable', opts);
158
162
  Object.defineProperty(SchemaTypeOptions.prototype, 'sparse', opts);
159
163
 
160
164
  /**
161
- * If truthy, Mongoose will build a text index on this path.
165
+ * If [truthy](https://masteringjs.io/tutorials/fundamentals/truthy), Mongoose
166
+ * will build a text index on this path.
162
167
  *
163
168
  * @api public
164
169
  * @property text
@@ -107,7 +107,7 @@ exports.getDiscriminatorByValue = getDiscriminatorByValue;
107
107
  * @param {Object} doc
108
108
  * @param {Object} fields
109
109
  *
110
- * @return {Model}
110
+ * @return {Document}
111
111
  */
112
112
  exports.createModel = function createModel(model, doc, fields, userProvidedFields) {
113
113
  model.hooks.execPreSync('createModel', doc);
@@ -280,6 +280,7 @@ SingleNestedPath.prototype.doValidateSync = function(value, scope, options) {
280
280
  * @param {String} name
281
281
  * @param {Schema} schema fields to add to the schema for instances of this sub-class
282
282
  * @param {String} [value] the string stored in the `discriminatorKey` property. If not specified, Mongoose uses the `name` parameter.
283
+ * @return {Function} the constructor Mongoose will use for creating instances of this discriminator model
283
284
  * @see discriminators /docs/discriminators.html
284
285
  * @api public
285
286
  */
@@ -196,8 +196,8 @@ SchemaArray.prototype.checkRequired = function checkRequired(value, doc) {
196
196
  */
197
197
 
198
198
  SchemaArray.prototype.enum = function() {
199
- let arr = this; /* eslint consistent-this: 0 */
200
- while (true) { /* eslint no-constant-condition: 0 */
199
+ let arr = this;
200
+ while (true) {
201
201
  const instance = get(arr, 'caster.instance');
202
202
  if (instance === 'Array') {
203
203
  arr = arr.caster;
@@ -9,6 +9,7 @@ const CastError = require('../error/cast');
9
9
  const EventEmitter = require('events').EventEmitter;
10
10
  const SchemaType = require('../schematype');
11
11
  const discriminator = require('../helpers/model/discriminator');
12
+ const get = require('../helpers/get');
12
13
  const util = require('util');
13
14
  const utils = require('../utils');
14
15
  const getConstructor = require('../helpers/discriminator/getConstructor');
@@ -54,6 +55,17 @@ function DocumentArrayPath(key, schema, options, schemaOptions) {
54
55
  return arr;
55
56
  });
56
57
  }
58
+
59
+ const parentSchemaType = this;
60
+ this.$embeddedSchemaType = new SchemaType(key + '.$', {
61
+ required: get(this, 'schemaOptions.required', false)
62
+ });
63
+ this.$embeddedSchemaType.cast = function(value, doc, init) {
64
+ return parentSchemaType.cast(value, doc, init)[0];
65
+ };
66
+ this.$embeddedSchemaType.$isMongooseDocumentArrayElement = true;
67
+ this.$embeddedSchemaType.caster = this.Constructor;
68
+ this.$embeddedSchemaType.schema = this.schema;
57
69
  }
58
70
 
59
71
  /**
@@ -135,6 +147,7 @@ function _createConstructor(schema, options, baseClass) {
135
147
  * @param {Schema} schema fields to add to the schema for instances of this sub-class
136
148
  * @param {String} [value] the string stored in the `discriminatorKey` property. If not specified, Mongoose uses the `name` parameter.
137
149
  * @see discriminators /docs/discriminators.html
150
+ * @return {Function} the constructor Mongoose will use for creating instances of this discriminator model
138
151
  * @api public
139
152
  */
140
153
 
package/lib/schema.js CHANGED
@@ -402,7 +402,6 @@ Schema.prototype.add = function add(obj, prefix) {
402
402
  // the `_id` option. This behavior never worked before 5.4.11 but numerous
403
403
  // codebases use it (see gh-7516, gh-7512).
404
404
  if (obj._id === false && prefix == null) {
405
- delete obj._id;
406
405
  this.options._id = false;
407
406
  }
408
407
 
@@ -417,6 +416,10 @@ Schema.prototype.add = function add(obj, prefix) {
417
416
  throw new TypeError('Invalid value for schema path `' + fullPath +
418
417
  '`, got value "' + obj[key] + '"');
419
418
  }
419
+ // Retain `_id: false` but don't set it as a path, re: gh-8274.
420
+ if (key === '_id' && obj[key] === false) {
421
+ continue;
422
+ }
420
423
 
421
424
  if (Array.isArray(obj[key]) && obj[key].length === 1 && obj[key][0] == null) {
422
425
  throw new TypeError('Invalid value for schema Array path `' + fullPath +
@@ -1179,16 +1182,7 @@ function getPositionalPathType(self, path) {
1179
1182
 
1180
1183
  if (i === last && val && !/\D/.test(subpath)) {
1181
1184
  if (val.$isMongooseDocumentArray) {
1182
- const oldVal = val;
1183
- val = new SchemaType(subpath, {
1184
- required: get(val, 'schemaOptions.required', false)
1185
- });
1186
- val.cast = function(value, doc, init) {
1187
- return oldVal.cast(value, doc, init)[0];
1188
- };
1189
- val.$isMongooseDocumentArrayElement = true;
1190
- val.caster = oldVal.caster;
1191
- val.schema = oldVal.schema;
1185
+ val = val.$embeddedSchemaType;
1192
1186
  } else if (val instanceof MongooseTypes.Array) {
1193
1187
  // StringSchema, NumberSchema, etc
1194
1188
  val = val.caster;
@@ -1637,8 +1631,8 @@ Schema.prototype.indexes = function() {
1637
1631
  * @param {String|Model} [options.ref] model name or model instance. Marks this as a [populate virtual](populate.html#populate-virtuals).
1638
1632
  * @param {String|Function} [options.localField] Required for populate virtuals. See [populate virtual docs](populate.html#populate-virtuals) for more information.
1639
1633
  * @param {String|Function} [options.foreignField] Required for populate virtuals. See [populate virtual docs](populate.html#populate-virtuals) for more information.
1640
- * @param {Boolean|Function} [options.justOne=false] Only works with populate virtuals. If truthy, will be a single doc or `null`. Otherwise, the populate virtual will be an array.
1641
- * @param {Boolean} [options.count=false] Only works with populate virtuals. If truthy, this populate virtual will contain the number of documents rather than the documents themselves when you `populate()`.
1634
+ * @param {Boolean|Function} [options.justOne=false] Only works with populate virtuals. If [truthy](https://masteringjs.io/tutorials/fundamentals/truthy), will be a single doc or `null`. Otherwise, the populate virtual will be an array.
1635
+ * @param {Boolean} [options.count=false] Only works with populate virtuals. If [truthy](https://masteringjs.io/tutorials/fundamentals/truthy), this populate virtual will contain the number of documents rather than the documents themselves when you `populate()`.
1642
1636
  * @return {VirtualType}
1643
1637
  */
1644
1638
 
package/lib/schematype.js CHANGED
@@ -664,7 +664,7 @@ SchemaType.prototype.get = function(fn) {
664
664
  * Product.on('error', handleError);
665
665
  *
666
666
  * @param {RegExp|Function|Object} obj validator function, or hash describing options
667
- * @param {Function} [obj.validator] validator function. If the validator function returns `undefined` or a truthy value, validation succeeds. If it returns falsy (except `undefined`) or throws an error, validation fails.
667
+ * @param {Function} [obj.validator] validator function. If the validator function returns `undefined` or a truthy value, validation succeeds. If it returns [falsy](https://masteringjs.io/tutorials/fundamentals/falsy) (except `undefined`) or throws an error, validation fails.
668
668
  * @param {String|Function} [obj.message] optional error message. If function, should return the error message as a string
669
669
  * @param {Boolean} [obj.propsParameter=false] If true, Mongoose will pass the validator properties object (with the `validator` function, `message`, etc.) as the 2nd arg to the validator function. This is disabled by default because many validators [rely on positional args](https://github.com/chriso/validator.js#validators), so turning this on may cause unpredictable behavior in external validators.
670
670
  * @param {String|Function} [errorMsg] optional error message. If function, should return the error message as a string
@@ -1265,7 +1265,7 @@ SchemaType._isRef = function(self, value, doc, init) {
1265
1265
  // - setting / pushing values after population
1266
1266
  const path = doc.$__fullPath(self.path);
1267
1267
  const owner = doc.ownerDocument ? doc.ownerDocument() : doc;
1268
- ref = owner.populated(path);
1268
+ ref = owner.populated(path) || doc.populated(self.path);
1269
1269
  }
1270
1270
 
1271
1271
  if (ref) {
@@ -634,24 +634,6 @@ class CoreMongooseArray extends Array {
634
634
  undefined, { skipDocumentArrayCast: true });
635
635
  const ret = [].push.apply(this, values);
636
636
 
637
- // If this is a document array, each element may contain single
638
- // populated paths, so we need to modify the top-level document's
639
- // populated cache. See gh-8247.
640
- if (this.isMongooseDocumentArray && parent.$__.populated != null) {
641
- const populatedPaths = Object.keys(parent.$__.populated).
642
- filter(p => p.startsWith(this[arrayPathSymbol] + '.'));
643
-
644
- for (const path of populatedPaths) {
645
- const remnant = path.slice((this[arrayPathSymbol] + '.').length);
646
- if (!Array.isArray(parent.$__.populated[path].value)) {
647
- continue;
648
- }
649
- for (const val of values) {
650
- parent.$__.populated[path].value.push(val.populated(remnant));
651
- }
652
- }
653
- }
654
-
655
637
  this._registerAtomic('$push', values);
656
638
  this._markModified();
657
639
  return ret;
@@ -172,6 +172,64 @@ class CoreDocumentArray extends CoreMongooseArray {
172
172
  }));
173
173
  }
174
174
 
175
+ /**
176
+ * Wraps [`Array#push`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/push) with proper change tracking.
177
+ *
178
+ * @param {Object} [args...]
179
+ * @api public
180
+ * @method push
181
+ * @memberOf MongooseDocumentArray
182
+ */
183
+
184
+ push() {
185
+ const ret = super.push.apply(this, arguments);
186
+
187
+ _updateParentPopulated(this);
188
+
189
+ return ret;
190
+ }
191
+
192
+ /**
193
+ * Pulls items from the array atomically.
194
+ *
195
+ * @param {Object} [args...]
196
+ * @api public
197
+ * @method pull
198
+ * @memberOf MongooseDocumentArray
199
+ */
200
+
201
+ pull() {
202
+ const ret = super.pull.apply(this, arguments);
203
+
204
+ _updateParentPopulated(this);
205
+
206
+ return ret;
207
+ }
208
+
209
+ /**
210
+ * Wraps [`Array#shift`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/unshift) with proper change tracking.
211
+ */
212
+
213
+ shift() {
214
+ const ret = super.shift.apply(this, arguments);
215
+
216
+ _updateParentPopulated(this);
217
+
218
+ return ret;
219
+ }
220
+
221
+ /**
222
+ * Wraps [`Array#splice`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice) with proper change tracking and casting.
223
+ */
224
+
225
+ splice() {
226
+ const ret = super.splice.apply(this, arguments);
227
+
228
+ _updateParentPopulated(this);
229
+
230
+ return ret;
231
+ }
232
+
175
233
  /**
176
234
  * Helper for console.log
177
235
  *
@@ -254,6 +312,29 @@ if (util.inspect.custom) {
254
312
  CoreDocumentArray.prototype.inspect;
255
313
  }
256
314
 
315
+ /*!
316
+ * If this is a document array, each element may contain single
317
+ * populated paths, so we need to modify the top-level document's
318
+ * populated cache. See gh-8247, gh-8265.
319
+ */
320
+
321
+ function _updateParentPopulated(arr) {
322
+ const parent = arr[arrayParentSymbol];
323
+ if (parent.$__.populated != null) {
324
+ const populatedPaths = Object.keys(parent.$__.populated).
325
+ filter(p => p.startsWith(arr[arrayPathSymbol] + '.'));
326
+
327
+ for (const path of populatedPaths) {
328
+ const remnant = path.slice((arr[arrayPathSymbol] + '.').length);
329
+ if (!Array.isArray(parent.$__.populated[path].value)) {
330
+ continue;
331
+ }
332
+
333
+ parent.$__.populated[path].value = arr.map(val => val.populated(remnant));
334
+ }
335
+ }
336
+ }
337
+
257
338
  /**
258
339
  * DocumentArray constructor
259
340
  *
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "5.7.7",
4
+ "version": "5.7.8",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",
@@ -102,10 +102,6 @@
102
102
  },
103
103
  "rules": {
104
104
  "comma-style": "error",
105
- "consistent-this": [
106
- "error",
107
- "_this"
108
- ],
109
105
  "indent": [
110
106
  "error",
111
107
  2,
@@ -118,6 +114,7 @@
118
114
  "no-buffer-constructor": "warn",
119
115
  "no-console": "off",
120
116
  "no-multi-spaces": "error",
117
+ "no-constant-condition": "off",
121
118
  "func-call-spacing": "error",
122
119
  "no-trailing-spaces": "error",
123
120
  "quotes": [