Skip to content

Commit a0b1f09

Browse files
NullVoxPopuliclaude
andcommitted
Move Meta mixin methods to standalone fns in @ember/object/mixin
The `addMixin` / `hasMixin` / `forEachMixins` methods only existed on `Meta` to be called by `@ember/object/mixin`. Keeping them on the class forced a static reference from `Meta` (reachable from the renderer through the property accessor / tag chain) into the classic `Mixin` graph. Move them out as standalone functions (`metaAddMixin` / `metaHasMixin` / `metaForEachMixins`) in `mixin.ts` itself, poking at `Meta`'s public `_mixins` and `parent` fields directly. With this split, bundles that don't import `@ember/object/mixin` get a cleaner `Meta` class — in the hello-world prod bundle the `addMixin` / `hasMixin` / `forEachMixins` identifiers go from present to fully absent, and `Mixin` references drop from 12 to 5. The methods were `@internal` and only called from `mixin.ts`, so this is a purely internal refactor. Verified: lint clean, type-checks pass, hello-world builds at 134.19 KB / 42.94 KB gzip (unchanged), classic v2-app-template tests 1/1 pass. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
1 parent 6899fa3 commit a0b1f09

2 files changed

Lines changed: 67 additions & 42 deletions

File tree

packages/@ember/-internals/meta/lib/meta.ts

Lines changed: 8 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -267,44 +267,14 @@ export class Meta {
267267
return undefined;
268268
}
269269

270-
/** @internal */
271-
addMixin(mixin: any) {
272-
assert(
273-
isDestroyed(this.source)
274-
? `Cannot add mixins of \`${toString(mixin)}\` on \`${toString(
275-
this.source
276-
)}\` call addMixin after it has been destroyed.`
277-
: '',
278-
!isDestroyed(this.source)
279-
);
280-
let set = this._getOrCreateOwnSet('_mixins');
281-
set.add(mixin);
282-
}
283-
284-
/** @internal */
285-
hasMixin(mixin: any) {
286-
return this._hasInInheritedSet('_mixins', mixin);
287-
}
288-
289-
/** @internal */
290-
forEachMixins(fn: Function) {
291-
let pointer: Meta | null = this;
292-
let seen: Set<any> | undefined;
293-
while (pointer !== null) {
294-
let set = pointer._mixins;
295-
if (set !== undefined) {
296-
seen = seen === undefined ? new Set() : seen;
297-
// TODO cleanup typing here
298-
set.forEach((mixin: any) => {
299-
if (!seen!.has(mixin)) {
300-
seen!.add(mixin);
301-
fn(mixin);
302-
}
303-
});
304-
}
305-
pointer = pointer.parent;
306-
}
307-
}
270+
// The mixin-tracking helpers (`addMixin` / `hasMixin` / `forEachMixins`)
271+
// moved to `@ember/object/mixin` as standalone functions
272+
// (`metaAddMixin` / `metaHasMixin` / `metaForEachMixins`). They poke
273+
// directly at the public `_mixins` / `parent` fields here. Splitting
274+
// them out keeps the `Meta` class — which is reachable from the
275+
// renderer via the property accessor + tag chain — free of references
276+
// to the classic `Mixin` machinery, so bundles that don't actually use
277+
// mixins can tree-shake `@ember/object/mixin` away.
308278

309279
/** @internal */
310280
writeDescriptors(subkey: string, value: any) {

packages/@ember/object/mixin.ts

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,61 @@
44
import { INIT_FACTORY } from '@ember/-internals/container/lib/container';
55
import type { Meta } from '@ember/-internals/meta/lib/meta';
66
import { meta as metaFor, peekMeta } from '@ember/-internals/meta/lib/meta';
7+
import { isDestroyed } from '@glimmer/destroyable';
8+
import toString from '@ember/-internals/utils/lib/to-string';
9+
10+
// Mixin-tracking helpers — moved out of `Meta` so the renderer-only
11+
// path (which reaches `Meta` through the tag/property graph) doesn't
12+
// pull a reference to `Mixin` into the bundle. They poke at `Meta`'s
13+
// public `_mixins` / `parent` fields directly.
14+
15+
16+
function metaAddMixin(meta: Meta, mixin: any): void {
17+
assert(
18+
isDestroyed(meta.source as object)
19+
? `Cannot add mixins of \`${toString(mixin)}\` on \`${toString(
20+
meta.source
21+
)}\` call addMixin after it has been destroyed.`
22+
: '',
23+
!isDestroyed(meta.source as object)
24+
);
25+
let set = meta._mixins ?? (meta._mixins = new Set());
26+
set.add(mixin);
27+
}
28+
29+
30+
function metaHasMixin(meta: Meta, mixin: any): boolean {
31+
let pointer: Meta | null = meta;
32+
while (pointer !== null) {
33+
let set = pointer._mixins;
34+
if (set !== undefined && set.has(mixin)) {
35+
return true;
36+
}
37+
pointer = pointer.parent;
38+
}
39+
return false;
40+
}
41+
42+
43+
function metaForEachMixins(meta: Meta, fn: (mixin: any) => void): void {
44+
let pointer: Meta | null = meta;
45+
46+
let seen: Set<any> | undefined;
47+
while (pointer !== null) {
48+
let set = pointer._mixins;
49+
if (set !== undefined) {
50+
seen = seen === undefined ? new Set() : seen;
51+
52+
set.forEach((mixin: any) => {
53+
if (!seen!.has(mixin)) {
54+
seen!.add(mixin);
55+
fn(mixin);
56+
}
57+
});
58+
}
59+
pointer = pointer.parent;
60+
}
61+
}
762
import { observerListenerMetaFor, ROOT, wrap } from '@ember/-internals/utils/lib/super';
863
import { assert } from '@ember/debug';
964
import { DEBUG } from '@glimmer/env';
@@ -247,10 +302,10 @@ function mergeMixins(
247302
);
248303

249304
if (MIXINS.has(currentMixin)) {
250-
if (meta.hasMixin(currentMixin)) {
305+
if (metaHasMixin(meta, currentMixin)) {
251306
continue;
252307
}
253-
meta.addMixin(currentMixin);
308+
metaAddMixin(meta, currentMixin);
254309

255310
let { properties, mixins } = currentMixin;
256311

@@ -592,7 +647,7 @@ export default class Mixin {
592647
return ret;
593648
}
594649

595-
meta.forEachMixins((currentMixin: Mixin) => {
650+
metaForEachMixins(meta, (currentMixin: Mixin) => {
596651
// skip primitive mixins since these are always anonymous
597652
if (!currentMixin.properties) {
598653
ret.push(currentMixin);
@@ -664,7 +719,7 @@ export default class Mixin {
664719
if (meta === null) {
665720
return false;
666721
}
667-
return meta.hasMixin(this);
722+
return metaHasMixin(meta, this);
668723
}
669724

670725
/** @internal */

0 commit comments

Comments
 (0)