diff --git a/packages/@ember/-internals/runtime/tests/mixins/accessor_test.js b/packages/@ember/-internals/runtime/tests/mixins/accessor_test.js new file mode 100644 index 00000000000..e7d3e04cdd3 --- /dev/null +++ b/packages/@ember/-internals/runtime/tests/mixins/accessor_test.js @@ -0,0 +1,56 @@ +import EmberObject from '@ember/object'; +import { moduleFor, RenderingTestCase } from 'internal-test-helpers'; + +moduleFor( + 'runtime: Mixin Accessors', + class extends RenderingTestCase { + ['@test works with getters'](assert) { + let value = 'building'; + + let Base = EmberObject.extend({ + get foo() { + if (value === 'building') { + throw Error('base should not be called yet'); + } + + return "base's foo"; + }, + }); + + // force Base to be finalized so its properties will contain `foo` + Base.proto(); + + class Child extends Base { + get foo() { + if (value === 'building') { + throw Error('child should not be called yet'); + } + + return "child's foo"; + } + } + + Child.proto(); + + let Grandchild = Child.extend({ + get foo() { + if (value === 'building') { + throw Error('grandchild should not be called yet'); + } + + return value; + }, + }); + + let instance = Grandchild.create(); + + value = 'done building'; + + assert.equal(instance.foo, 'done building', 'getter defined correctly'); + + value = 'changed value'; + + assert.equal(instance.foo, 'changed value', 'the value is a real getter, not a snapshot'); + } + } +); diff --git a/packages/@ember/object/mixin.ts b/packages/@ember/object/mixin.ts index 180707e29bb..25affc808f8 100644 --- a/packages/@ember/object/mixin.ts +++ b/packages/@ember/object/mixin.ts @@ -8,12 +8,14 @@ import { guidFor, observerListenerMetaFor, ROOT, wrap } from '@ember/-internals/ import { assert } from '@ember/debug'; import { DEBUG } from '@glimmer/env'; import { _WeakSet } from '@glimmer/util'; -import type { - ComputedDecorator, - ComputedPropertyGetter, - ComputedPropertyObj, - ComputedPropertySetter, - ComputedDescriptor, +import { + type ComputedDecorator, + type ComputedPropertyGetter, + type ComputedPropertyObj, + type ComputedPropertySetter, + type ComputedDescriptor, + isAccessor, + isClassicDecorator, } from '@ember/-internals/metal'; import { ComputedProperty, @@ -235,7 +237,7 @@ function mergeMixins( keys: string[], keysWithSuper: string[] ): void { - let currentMixin; + let currentMixin: MixinLike | undefined; for (let i = 0; i < mixins.length; i++) { currentMixin = mixins[i]; @@ -309,12 +311,17 @@ function mergeProps( let desc = meta.peekDescriptors(key); if (desc === undefined) { - // The superclass did not have a CP, which means it may have - // observers or listeners on that property. - let prev = (values[key] = base[key]); - - if (typeof prev === 'function') { - updateObserversAndListeners(base, key, prev, false); + // If the value is a classic decorator, we don't want to actually + // access it, because that will execute the decorator while we're + // building the class. + if (!isClassicDecorator(value)) { + // The superclass did not have a CP, which means it may have + // observers or listeners on that property. + let prev = (values[key] = base[key]); + + if (typeof prev === 'function') { + updateObserversAndListeners(base, key, prev, false); + } } } else { descs[key] = desc; diff --git a/tsconfig.json b/tsconfig.json index fb94bf9d432..7e30b8b1278 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,6 +4,6 @@ "outDir": "dist", "noEmit": true }, - "include": ["packages/**/*.ts"], + "include": ["packages/**/*.ts", "packages/**/*.js"], "exclude": ["dist", "node_modules", "tmp", "types"] }