diff --git a/lib/document.js b/lib/document.js index 52f39f4328..55b916893e 100644 --- a/lib/document.js +++ b/lib/document.js @@ -4256,24 +4256,25 @@ function applySchemaTypeTransforms(self, json) { for (const path of paths) { const schematype = schema.paths[path]; - if (typeof schematype.options.transform === 'function') { + const topLevelTransformFunction = schematype.options.transform ?? schematype.constructor?.defaultOptions?.transform; + const embeddedSchemaTypeTransformFunction = schematype.$embeddedSchemaType?.options?.transform + ?? schematype.$embeddedSchemaType?.constructor?.defaultOptions?.transform; + if (typeof topLevelTransformFunction === 'function') { const val = self.$get(path); if (val === undefined) { continue; } - const transformedValue = schematype.options.transform.call(self, val); + const transformedValue = topLevelTransformFunction.call(self, val); throwErrorIfPromise(path, transformedValue); utils.setValue(path, transformedValue, json); - } else if (schematype.$embeddedSchemaType != null && - typeof schematype.$embeddedSchemaType.options.transform === 'function') { + } else if (typeof embeddedSchemaTypeTransformFunction === 'function') { const val = self.$get(path); if (val === undefined) { continue; } const vals = [].concat(val); - const transform = schematype.$embeddedSchemaType.options.transform; for (let i = 0; i < vals.length; ++i) { - const transformedValue = transform.call(self, vals[i]); + const transformedValue = embeddedSchemaTypeTransformFunction.call(self, vals[i]); vals[i] = transformedValue; throwErrorIfPromise(path, transformedValue); } diff --git a/package.json b/package.json index da67323104..1f11693d60 100644 --- a/package.json +++ b/package.json @@ -30,8 +30,8 @@ "devDependencies": { "@babel/core": "7.26.0", "@babel/preset-env": "7.26.0", - "@typescript-eslint/eslint-plugin": "^8.18.0", - "@typescript-eslint/parser": "^8.18.0", + "@typescript-eslint/eslint-plugin": "8.18.0", + "@typescript-eslint/parser": "8.18.0", "acquit": "1.3.0", "acquit-ignore": "0.2.1", "acquit-require": "0.1.1", diff --git a/test/document.test.js b/test/document.test.js index 4317795223..e0889ec1d2 100644 --- a/test/document.test.js +++ b/test/document.test.js @@ -14225,6 +14225,46 @@ describe('document', function() { assert.strictEqual(duplicateKeyError.message, 'Email must be unique'); assert.strictEqual(duplicateKeyError.cause.code, 11000); }); + + it('supports global transforms per schematype (gh-15084)', async function() { + class SchemaCustomType extends mongoose.SchemaType { + constructor(key, options) { + super(key, options, 'CustomType'); + } + + cast(value) { + if (value === null) return null; + return new CustomType(value); + } + } + SchemaCustomType.schemaName = 'CustomType'; + + class CustomType { + constructor(value) { + this.value = value; + } + } + + mongoose.Schema.Types.CustomType = SchemaCustomType; + + const Model = db.model( + 'Test', + new mongoose.Schema({ + value: { type: mongoose.Schema.Types.CustomType } + }) + ); + + const _id = new mongoose.Types.ObjectId('0'.repeat(24)); + const doc = new Model({ _id }); + doc.value = 1; + + mongoose.Schema.Types.CustomType.set('transform', v => v == null ? v : v.value); + + assert.deepStrictEqual(doc.toJSON(), { _id, value: 1 }); + assert.deepStrictEqual(doc.toObject(), { _id, value: 1 }); + + delete mongoose.Schema.Types.CustomType; + }); }); describe('Check if instance function that is supplied in schema option is available', function() {