Skip to content

Add a modern-only build variant of ember-source#21504

Draft
wagenet wants to merge 26 commits into
emberjs:mainfrom
wagenet:modern-build-variant
Draft

Add a modern-only build variant of ember-source#21504
wagenet wants to merge 26 commits into
emberjs:mainfrom
wagenet:modern-build-variant

Conversation

@wagenet

@wagenet wagenet commented Jul 16, 2026

Copy link
Copy Markdown
Member

This adds a second, opt-in build of ember-source that contains only modern (Octane and later) patterns. The classic object model — EmberObject, Mixin, computed properties, observers-as-public-API, the proxies, EmberArray, Evented — and classic components are absent from its module graph entirely. Full documentation is in MODERN-BUILD.md.

Opening as a draft to gather feedback on the approach before polishing further. Some of what's here (particularly the removal semantics and eventual deprecation story) would need to go through the RFC process before any of it ships as more than an experiment.

What's in the box

  • dist/modern/{dev,prod}, published inside the regular ember-source package under an ember-modern export condition. Apps opt in with resolve.conditions: ['ember-modern', ...] in their Vite config; nothing changes for anyone who doesn't.
  • @ember/legacy-features: three flags (CLASSIC_OBJECT_MODEL, CLASSIC_COMPONENTS, CONTROLLER_QUERY_PARAMS), each owning a coordinated set of build levers — debug-macro folding, resolve-time module swaps, and entrypoint prunes (see legacySections() in rollup.config.mjs). Other combinations can be built locally with EMBER_LEGACY_FLAGS=... pnpm build:js.
  • Controllers and query params stay for now, isolated behind CONTROLLER_QUERY_PARAMS with the observer machinery contained in one seam (@ember/routing/lib/qp-observers.ts). When a Route Manager-based QP replacement exists (post-RFC jQuery.extend(deep, []) fails with Ember.USE_ACCESSORS (infinite loop) #1169), removing them is a flag flip rather than a re-plumbing.

Mainline changes

The first half of the commits de-entangle framework classes from the classic object model in a way that's behavior-neutral for the regular build (FrameworkObject still extends EmberObject there):

  • Router, RouterService, Route, Controller, Engine, EngineInstance, Namespace, the Locations, and the testing Adapter now extend FrameworkObject directly, with module-scope reopen() calls folded into class bodies or prototype-assigned defaults.
  • Evented usage is replaced by an owned emitter behind the same public on/one/off/trigger/has methods.
  • Router state (currentURL, currentRoute, etc.) is @tracked, with native getters replacing the readOnly aliases.

The modern build then swaps a small number of files at the rollup resolve level — most importantly FrameworkObject itself for a native class that satisfies the container's static create(props) protocol via @glimmer/destroyable.

All breaks are limited to legacy imports and are intended to be codemoddable; the table in MODERN-BUILD.md maps each removal to its replacement. The one semver edge worth calling out for the regular build: init() overrides on framework classes (e.g. custom Locations) must call super.init(...arguments) — this was already the documented contract, and it's now asserted in debug builds.

Verification

  • Existing suite: 9443 tests, no failures — this is the regression gate for the mainline refactors.
  • pnpm test:modern: 3229 tests running against the modern variant's actual module swaps and folded flags (curated allowlist in index-modern.html, designed to grow).
  • bin/assert-modern-dist.mjs: CI-enforced scan asserting no classic-module identifiers reach dist/modern/prod.
  • smoke-tests/v2-app-modern-template: a real Embroider/Vite app consuming the variant, with acceptance tests covering routing, <LinkTo> with query params, controller QP sync, router service events, service injection, and <Input>.
  • A new modern-variant CI job runs all of the above.
  • Dist sizes: dev 2.7M → 2.3M, prod 2.6M → 2.2M (tree-shaken apps should save more).

Known limitations

  • @ember/test-helpers calls EmberObject.extend at module scope in its mock-owner support, which breaks dev/test builds of modern-variant apps. The smoke app ships a lazy-construction shim for that one module as a workaround; the real fix belongs upstream.
  • ember-inspector support is degraded (the DataAdapter depends on the classic object model).
  • Runtime deprecations for the legacy sections in the standard build are wired conceptually but intentionally not included — they need deprecation RFCs first.

Best reviewed commit by commit; each one landed with the full suite green.

🤖 Generated with Claude Code

wagenet and others added 22 commits July 15, 2026 16:21
A minimal owned event emitter matching the observable semantics of the
Evented mixin (reverse-order dispatch, once-removal before invocation,
host as default target, string method resolution), so framework classes
can expose the classic on/one/off/trigger/has API by composition instead
of inheriting from Evented.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Granular build-time flags (CLASSIC_OBJECT_MODEL, CLASSIC_COMPONENTS,
CONTROLLER_QUERY_PARAMS) gating legacy sections of Ember, folded by
babel-plugin-debug-macros like @ember/canary-features. The broccoli
plugin validates flag combinations (classic components require the
classic object model) and parses EMBER_LEGACY_FLAGS for custom builds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
EmberRouter and RouterService extend FrameworkObject directly instead of
EmberObject.extend(Evented); the Evented API is preserved by real
on/one/off/trigger/has methods backed by an owned EventedEmitter, so
routeWillChange/routeDidChange subscriptions are unchanged. The reopen
blocks are folded into class bodies (prototype-assigned defaults keep
.extend() overrides working), the url computed becomes a native getter,
and router URL state (currentURL, currentRouteName, currentPath,
currentRoute, currentState, targetState) becomes @Tracked.

RouterService's and RoutingService's readOnly aliases over that state
become native getters. LinkTo previously invalidated via the alias tag
(tagFor(routing, 'currentState')), which nothing dirties once the
aliases are gone, so it now entangles by reading routing.currentState.

Array-valued query params go through a new makeQPArray seam so variant
builds can drop the EmberArray wrapping.

Overriding init on the router now requires calling super.init, the same
contract as every other framework object.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Route extends FrameworkObject instead of
EmberObject.extend(ActionHandler, Evented): ActionHandler's send() is
implemented directly, activate/deactivate events go through an owned
EventedEmitter, the 200-line reopen is folded into the class body with
prototype-assigned defaults (mergedProperties reproduces what
ActionHandler and the reopen contributed, so classic .extend() keeps
merging actions/queryParams), and the @computed _store/_qp getters
become a plain getter and a per-instance memo respectively.

Controller's behavior moves into a native class in -base.ts: init,
send(), _qpChanged, and a model accessor whose setter skips notification
for already-cached values and is registered in COMPUTED_SETTERS (like
@Tracked setters) so metal set takes the fast path — the DEBUG
mandatory-setter pre-read otherwise consumes the tag mid-write and trips
the backtracking-rerender assertion on {{mount}}'s model updates. The
classic ControllerMixin remains public API, defined in index.ts but no
longer applied to the class.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…cher

Namespace extends FrameworkObject, Engine extends Namespace directly,
and EngineInstance extends FrameworkObject; the RegistryProxy and
ContainerProxy mixins become plain method bags (owner-proxies.ts)
assigned onto the prototypes, with the public types still provided by
the interface merges. buildInitializerMethod drops reopenClass in favor
of direct static assignment.

EventDispatcher registration and setup are gated behind the
CLASSIC_COMPONENTS flag: the dispatcher exists solely to drive classic
components' element event methods, so builds without classic components
compile it out.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The action decorator moves to @ember/object/-action (re-exported from
the @ember/object barrel), so it no longer shares a module with
EmberObject/computed/observer. CHAIN_PASS_THROUGH moves out of
chain-tags into its own module so tracked's import closure stops pulling
the observer-chain machinery.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The three Location classes and ContainerDebugAdapter extend
FrameworkObject (their init overrides now call super.init — HashLocation
previously skipped it and HistoryLocation used this._super, a silent
no-op in a native class). typeOf's CoreObject detection moves behind a
classic-detect seam so variant builds can swap it, and {{mount}} sets
the controller model via imported set instead of Observable's .set.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Alternate implementations swapped in at the build level for variants
without the classic object model or classic components:

- FrameworkObject as a plain native class satisfying the container's
  static create(props) protocol and the destroyable lifecycle, with the
  super.init contract preserved in DEBUG
- @ember/object, @ember/object/internals, @ember/component,
  @ember/controller, and the glimmer/views/runtime barrels minus their
  classic exports (dropping the glimmer Component export is what removes
  the curly component implementation from the rolled-up graph)
- a computed-free @service/@controller injection (plain prototype getter
  with per-instance caching, settable for tests)
- no-op EventDispatcher (kept because @ember/test-helpers instantiates
  one for mock owners), plain-array query params, classic-detect stubs,
  a contentFor stub, and a test Adapter with a minimal extend/create
  protocol (ember-qunit subclasses it via Adapter.extend with an init
  that never calls super, so it must not inherit the framework-object
  init contract)

ember-testing exposes its subpaths and imports the adapter via a bare
specifier so the swap can apply.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Each legacy-features flag owns three coordinated build levers in
rollup.config.mjs: module swaps (applied to bare import specifiers at
resolve time), entrypoint swaps (replacing a published module's
implementation), and entrypoint prunes. Disabling a section activates
all of them; the modern build (all flags off except
CONTROLLER_QUERY_PARAMS) emits to dist/modern/{dev,prod}, selected via a
nested ember-modern export condition so Embroider/Vite apps opt in with
resolve.conditions. EMBER_LEGACY_FLAGS builds custom combinations to
dist/custom.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
bin/assert-modern-dist.mjs scans dist/modern/prod for identifiers unique
to the classic modules (comments stripped; assert strings are useless as
markers since prod strips them), with the query-params observer
machinery explicitly allowlisted until controllers can be removed.

smoke-tests/v2-app-modern-template is v2-app-template opted into the
ember-modern condition, minus @embroider/legacy-inspector-support (it
imports the @ember/-internals/metal barrel, which the modern variant
does not ship), plus integration coverage for the computed-free
@service injection. Its test build currently requires a patch to
@ember/test-helpers, whose mock-owner module calls EmberObject.extend at
module scope.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…I job

- addQueryParamsObservers and finalizeQueryParamChange reach the observer
  machinery only through @ember/routing/lib/qp-observers, the designated
  swap point for removing observers once a controller-free query params
  implementation exists (RFC emberjs#1169)
- BOUNDS moves from the curly component manager to glimmer/lib/utils so
  the renderer stops importing classic-component territory
- a modern-variant CI job builds the dists, runs the forbidden-modules
  scan, reports sizes, and production-builds the modern smoke app (its
  dev/test builds await an @ember/test-helpers fix for the module-scope
  EmberObject.extend in its mock-owner support)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
MODERN=1 builds the curated suite in index-modern.html against the
modern variant's module swaps, with the legacy-feature flags folded to
their modern values (babel.test.config.mjs) and testem pointed at the
modern test page. internal-test-helpers gains a modern-safe subset
barrel, since its full barrel re-exports classic test-case classes.

The initial allowlist — the @Glimmer VM suites, @ember/-internals/utils,
and @ember/runloop — runs 3074 tests against the swapped modules; grow
the index-modern.html globs as more suites become clean of classic
patterns in their test code. Wired into CI as `pnpm test:modern` in the
modern-variant job.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
How to opt in (the ember-modern export condition), the flag/section
model and custom builds, the removed-vs-replacement table with codemod
pointers, known ecosystem limitations, and the verification tooling.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The reopen call and the no-op this._super in initState only work when
FrameworkObject is backed by the classic object model, so the modern
variant threw at module evaluation. Prototype-assigned defaults keep
.extend() overrides working in the standard build.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ember/test-helpers calls EmberObject.extend at module scope in its
mock-owner support, which the modern variant cannot evaluate; a vite
load hook serves a lazy-construction shim so dev/test builds of
modern-variant apps work (removable once upstream makes the load lazy).

New smoke tests: an acceptance test covering NoneLocation, LinkTo with
@query, the controller query-param round trip, and router service
events, plus an <Input> two-way binding test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds container, string, deprecations, meta, instrumentation,
template-compiler, and the modern-clean debug/utils/routing suites to
index-modern.html (3229 tests, up from 3074), exporting buildOwner,
factory, and moduleForDevelopment from the modern subset of
internal-test-helpers. Two router tests now use plain property access
instead of router.get, which behaves identically in both builds.

The modern-variant CI job runs the smoke app's dev-mode test suite now
that the build-registry shim unblocks it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n script

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
renderer.ts's RootComponentDefinition import (only reachable through the
classic Component's appendTo) dragged the curly component manager into
the modern and custom dists. The import is now a bare specifier swapped
for a throwing stub when CLASSIC_COMPONENTS is disabled, and the dist
scan gained classic-component markers (CurlyComponentManager,
finalEventNameMapping) so this class of leak fails CI.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The dist scan now also indicts ObjectProxy, the -proxy machinery,
MutableArray, and NativeArray, and documents why mixin-built modules
need no markers of their own (they trip applyMixin/CoreObject
transitively). Three comments that described code in terms of what it
replaced now state the invariant directly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…Registry

The type-tests run against freshly built published types in CI and
caught three regressions:

- Route, EmberRouter, and RouterService lost Evented's
  (name, target, method) overloads on on/one/off and the any return on
  trigger. The emitter already supports targets at runtime; the
  signatures now match the Evented interface again.
- Controller.model as a class-body accessor emits as an accessor in the
  published types, making the common 'model' property declaration in
  subclasses a TS2610 error. The accessor now lives on the prototype via
  defineProperty, typed as a property through the ControllerMixin
  interface merge.
- The controller Registry interface moved to -base and was re-exported
  from the barrel, which silently broke every
  'declare module @ember/controller { interface Registry }'
  augmentation: module augmentation only merges with interfaces
  declared in the augmented module. Registry is declared in the barrel
  again, with a type-only re-export in the modern entrypoint.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The module-scope COMPUTED_SETTERS registration mutates an imported Set,
which rollup rightly treats as a cross-module side effect — pinning the
module (and the Controller class) in any bundle. The model accessor
definition and its setter registration now run in a class static block,
so both drop with the class when nothing uses Controller. Caught by the
tree-shakability snapshot test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@NullVoxPopuli

Copy link
Copy Markdown
Contributor

📊 Size report

Tarball size1.2 MB1.8 MB

dist/dev   1%↑

File Before (Size / Brotli) After (Size / Brotli)
Total (Includes all files) 2.1 MB / 498 kB 1%↑2.1 MB / 2%↑507.7 kB
Show files (44 files)
File Before (Size / Brotli) After (Size / Brotli)
./packages/@ember/-internals/routing/index.js 599 B / 202 B 29%↑772 B / 27%↑256 B
./packages/@ember/-internals/runtime/index-modern.js 341 B / 167 B
./packages/@ember/-internals/runtime/lib/mixins/-proxy-modern.js 351 B / 180 B
./packages/@ember/-internals/utils/index.js 1.7 kB / 651 B 6%↑1.8 kB / 4%↑676 B
./packages/@ember/-internals/views/index-modern.js 391 B / 185 B
./packages/@ember/-internals/views/lib/system/event_dispatcher_modern.js 613 B / 291 B
./packages/@ember/application/index.js 35.2 kB / 8.8 kB 0.7%↑35.5 kB / 0.8%↑8.8 kB
./packages/@ember/component/index-modern.js 1.2 kB / 388 B
./packages/@ember/component/index.js 44.8 kB / 10.3 kB -1.58%↓44.1 kB / -1.64%↓10.2 kB
./packages/@ember/controller/-base.js 6 kB / 2 kB
./packages/@ember/controller/index-modern.js 60 B / 64 B
./packages/@ember/controller/index.js 4.8 kB / 1.6 kB -18.8%↓3.9 kB / -13.8%↓1.4 kB
./packages/@ember/engine/instance.js 6.9 kB / 2.1 kB 9%↑7.6 kB / 9%↑2.3 kB
./packages/@ember/engine/lib/owner-proxies.js 1.8 kB / 452 B
./packages/@ember/legacy-features/index.js 1.1 kB / 473 B
./packages/@ember/object/-action.js 4 kB / 1.2 kB
./packages/@ember/object/-internals-modern.js 3.3 kB / 1.1 kB
./packages/@ember/object/index-modern.js 482 B / 179 B
./packages/@ember/object/index.js 7.6 kB / 2.1 kB -48.8%↓3.9 kB / -42.5%↓1.2 kB
./packages/@ember/object/internals-modern.js 81 B / 81 B
./packages/@ember/routing/-internals.js 736 B / 248 B -37%↓464 B / -38.7%↓152 B
./packages/@ember/routing/index.js 19.4 kB / 4.6 kB 1%↑19.6 kB / 2%↑4.7 kB
./packages/@ember/routing/lib/qp-array-modern.js 216 B / 122 B
./packages/@ember/routing/lib/qp-array.js 399 B / 213 B
./packages/@ember/routing/lib/qp-observers.js 692 B / 305 B
./packages/@ember/routing/lib/routing-service.js 4.5 kB / 1.4 kB -11.8%↓4 kB / -15.8%↓1.1 kB
./packages/@ember/routing/none-location.js 3.1 kB / 997 B 5%↑3.3 kB / 4%↑1 kB
./packages/@ember/routing/route.js 60.3 kB / 13.4 kB 1%↑60.9 kB / 2%↑13.6 kB
./packages/@ember/routing/router-service.js 25.1 kB / 5.3 kB -1.82%↓24.6 kB / -2.48%↓5.2 kB
./packages/@ember/routing/router.js 52 kB / 11.7 kB 4%↑54.2 kB / 4%↑12.2 kB
./packages/@ember/utils/lib/classic-detect-modern.js 297 B / 136 B
./packages/@ember/utils/lib/classic-detect.js 459 B / 235 B
./packages/ember-testing/lib/adapters/adapter-modern.js 1.3 kB / 491 B
./packages/shared-chunks/api-{hash}.js 26.2 kB / 5.8 kB -61.6%↓10 kB / -63.6%↓2.1 kB
./packages/shared-chunks/chain-tags-uWSImHzB.js 7.6 kB / 2.2 kB
./packages/shared-chunks/computed-{hash}.js 33.9 kB / 7.9 kB -17.3%↓28 kB / -17.4%↓6.5 kB
./packages/shared-chunks/curly-{hash}.js 21.5 kB / 5.1 kB 1%↑21.8 kB / 2%↑5.2 kB
./packages/shared-chunks/evented-emitter-BNvVhpL1.js 3.6 kB / 1 kB
./packages/shared-chunks/guid-Cbq2sNV_.js 3.2 kB / 1.1 kB
./packages/shared-chunks/internals-modern-Cbq2sNV_.js 3.2 kB / 1.1 kB
./packages/shared-chunks/observers-{hash}.js 6.8 kB / 1.6 kB 89%↑12.9 kB / 98%↑3.1 kB
./packages/shared-chunks/property_events-q_xf6eed.js 6.4 kB / 1.9 kB
./packages/shared-chunks/tags-RQ6dDyFG.js 1.9 kB / 735 B
./packages/shared-chunks/template-{hash}.js 491 B / 203 B 118%↑1.1 kB / 98%↑401 B

dist/prod   1%↑

File Before (Size / Brotli) After (Size / Brotli)
Total (Includes all files) 1.9 MB / 455.4 kB 1%↑1.9 MB / 2%↑464.6 kB
Show files (41 files)
File Before (Size / Brotli) After (Size / Brotli)
./packages/@ember/-internals/routing/index.js 568 B / 198 B 58%↑896 B / 43%↑284 B
./packages/@ember/-internals/runtime/index-modern.js 341 B / 167 B
./packages/@ember/-internals/runtime/lib/mixins/-proxy-modern.js 351 B / 180 B
./packages/@ember/-internals/utils/index.js 2.6 kB / 962 B 4%↑2.7 kB / -1.04%↓952 B
./packages/@ember/-internals/views/index-modern.js 391 B / 185 B
./packages/@ember/-internals/views/lib/system/event_dispatcher_modern.js 613 B / 291 B
./packages/@ember/application/index.js 31.9 kB / 8.3 kB 1%↑32.3 kB / 2%↑8.4 kB
./packages/@ember/component/index-modern.js 1.1 kB / 363 B
./packages/@ember/component/index.js 40.6 kB / 9.5 kB -1.51%↓39.9 kB / -1.63%↓9.3 kB
./packages/@ember/controller/-base.js 5.6 kB / 1.9 kB
./packages/@ember/controller/index-modern.js 60 B / 64 B
./packages/@ember/controller/index.js 4.8 kB / 1.6 kB -18.8%↓3.9 kB / -13.8%↓1.4 kB
./packages/@ember/engine/instance.js 6.5 kB / 2 kB 10%↑7.1 kB / 9%↑2.2 kB
./packages/@ember/engine/lib/owner-proxies.js 1.6 kB / 396 B
./packages/@ember/legacy-features/index.js 1.1 kB / 473 B
./packages/@ember/object/-action.js 3.2 kB / 983 B
./packages/@ember/object/-internals-modern.js 2.7 kB / 936 B
./packages/@ember/object/index-modern.js 482 B / 177 B
./packages/@ember/object/index.js 6 kB / 1.8 kB -49.9%↓3 kB / -42%↓1 kB
./packages/@ember/object/internals-modern.js 81 B / 81 B
./packages/@ember/routing/index.js 15.6 kB / 3.9 kB 1%↑15.8 kB / 2%↑3.9 kB
./packages/@ember/routing/lib/qp-array-modern.js 216 B / 122 B
./packages/@ember/routing/lib/qp-array.js 399 B / 213 B
./packages/@ember/routing/lib/qp-observers.js 692 B / 306 B
./packages/@ember/routing/lib/routing-service.js 4 kB / 1.2 kB -8.11%↓3.6 kB / -11.2%↓1 kB
./packages/@ember/routing/none-location.js 2.7 kB / 850 B 6%↑2.8 kB / 4%↑886 B
./packages/@ember/routing/route.js 54.8 kB / 12.1 kB 1%↑55.4 kB / 2%↑12.4 kB
./packages/@ember/routing/router-service.js 23.8 kB / 5.1 kB -1.09%↓23.6 kB / -1.19%↓5 kB
./packages/@ember/routing/router.js 44.1 kB / 10.2 kB 5%↑46.3 kB / 4%↑10.7 kB
./packages/@ember/utils/lib/classic-detect-modern.js 297 B / 136 B
./packages/@ember/utils/lib/classic-detect.js 459 B / 235 B
./packages/ember-testing/lib/adapters/adapter-modern.js 1.3 kB / 491 B
./packages/shared-chunks/chain-tags-B2J7DsxO.js 6.9 kB / 2 kB
./packages/shared-chunks/computed-{hash}.js 27.9 kB / 6.5 kB -19.2%↓22.6 kB / -20%↓5.2 kB
./packages/shared-chunks/curly-{hash}.js 17.2 kB / 4.3 kB 2%↑17.5 kB / 2%↑4.4 kB
./packages/shared-chunks/evented-emitter-CYePuS74.js 3 kB / 838 B
./packages/shared-chunks/guid-Cbq2sNV_.js 3.2 kB / 1.1 kB
./packages/shared-chunks/internals-modern-Cbq2sNV_.js 3.2 kB / 1.1 kB
./packages/shared-chunks/observers-{hash}.js 6.8 kB / 1.6 kB 85%↑12.6 kB / 93%↑3 kB
./packages/shared-chunks/property_events-Dbu02U0B.js 5.7 kB / 1.7 kB
./packages/shared-chunks/tags-DjTuKuxi.js 1.4 kB / 555 B

smoke-tests/v2-app-template/dist   1%↑

File Before (Size / Brotli) After (Size / Brotli)
./assets/main-{hash}.js 308.4 kB / 82.8 kB 0.3%↑309.4 kB / 0.05%↑82.8 kB
./assets/modules-{hash}.js 40.3 kB / 12.1 kB 6%↑42.9 kB / 6%↑12.8 kB
Total (Includes all files) 350.3 kB / 95.6 kB 1%↑353.9 kB / 0.8%↑96.4 kB

smoke-tests/v2-app-hello-world-template/dist   0.03%↑

File Before (Size / Brotli) After (Size / Brotli)
Total (Includes all files) 132.6 kB / 37 kB 0.03%↑132.6 kB / 0.06%↑37.1 kB

🤖 This report was automatically generated by wyvox/pkg-size

from: https://github.com/emberjs/ember.js/actions/runs/29518464187/job/87689314974?pr=21504

wagenet and others added 2 commits July 16, 2026 10:43
They only ever load through a variant's module/entrypoint swaps, so
emitting them into dist/dev and dist/prod added ~10 kB of dead files to
the standard build (flagged by the CI size report).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The build writes this manifest from the standard dist's entrypoints, so
the previous commit's input prune has to land here too (caught by the
package preparation CI check).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@wagenet

wagenet commented Jul 16, 2026

Copy link
Copy Markdown
Member Author

@NullVoxPopuli the build size delta is the new modern build itself.

wagenet and others added 2 commits July 16, 2026 11:14
The report's app-level sections are where the modern variant should
show up, but both apps consumed the standard build. The size job now
also builds the modern full-app template and a new hello-world clone
that opts into the ember-modern condition. Locally: hello-world
132.3 kB -> 117.8 kB (-11%), full app 352.3 kB -> 269.2 kB (-24%).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Rather than a cloned app, the size report builds the same hello-world
template twice — the second time with EMBER_MODERN=1 setting the
ember-modern resolve condition, emitted to dist-modern. The full-app
comparison keeps its own template: v2-app-template is regenerated from
the blueprint on a schedule, and its inspector support cannot build
against the modern variant.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@wagenet

wagenet commented Jul 16, 2026

Copy link
Copy Markdown
Member Author

Note

Refreshed size report from the latest run — fork PRs can't auto-update the comment, so this is the CI output relayed by Claude on @wagenet's behalf. Changes since the report above: the *-modern modules are no longer emitted into the standard dist, and the two modern app sections were added (hello-world is the identical app rebuilt with the ember-modern condition).

📊 Size report

Tarball size1.2 MB1.8 MB

dist/dev   0.8%↑

File Before (Size / Brotli) After (Size / Brotli)
Total (Includes all files) 2.1 MB / 498 kB 0.8%↑2.1 MB / 1%↑503.7 kB
Show files (24 files)
File Before (Size / Brotli) After (Size / Brotli)
./packages/@ember/-internals/routing/index.js 599 B / 202 B 29%↑772 B / 29%↑260 B
./packages/@ember/application/index.js 35.2 kB / 8.8 kB 0.7%↑35.5 kB / 0.8%↑8.8 kB
./packages/@ember/controller/-base.js 6 kB / 2 kB
./packages/@ember/controller/index.js 4.8 kB / 1.6 kB -18.8%↓3.9 kB / -13.8%↓1.4 kB
./packages/@ember/engine/instance.js 6.9 kB / 2.1 kB 9%↑7.5 kB / 9%↑2.3 kB
./packages/@ember/engine/lib/owner-proxies.js 1.8 kB / 452 B
./packages/@ember/legacy-features/index.js 1.1 kB / 473 B
./packages/@ember/object/-action.js 4 kB / 1.2 kB
./packages/@ember/object/index.js 7.6 kB / 2.1 kB -49.7%↓3.8 kB / -43.2%↓1.2 kB
./packages/@ember/routing/-internals.js 736 B / 248 B -37%↓464 B / -38.7%↓152 B
./packages/@ember/routing/index.js 19.4 kB / 4.6 kB 1%↑19.6 kB / 2%↑4.7 kB
./packages/@ember/routing/lib/qp-array.js 399 B / 213 B
./packages/@ember/routing/lib/qp-observers.js 692 B / 305 B
./packages/@ember/routing/lib/routing-service.js 4.5 kB / 1.4 kB -11.8%↓4 kB / -15.8%↓1.1 kB
./packages/@ember/routing/none-location.js 3.1 kB / 997 B 5%↑3.3 kB / 4%↑1 kB
./packages/@ember/routing/route.js 60.3 kB / 13.4 kB 1%↑60.9 kB / 2%↑13.6 kB
./packages/@ember/routing/router-service.js 25.1 kB / 5.3 kB -1.82%↓24.6 kB / -2.48%↓5.2 kB
./packages/@ember/routing/router.js 52 kB / 11.7 kB 4%↑54.2 kB / 3%↑12.1 kB
./packages/@ember/utils/lib/classic-detect.js 459 B / 235 B
./packages/shared-chunks/chain-tags-uWSImHzB.js 7.6 kB / 2.2 kB
./packages/shared-chunks/curly-{hash}.js 21.5 kB / 5.1 kB 1%↑21.8 kB / 2%↑5.2 kB
./packages/shared-chunks/evented-emitter-BNvVhpL1.js 3.6 kB / 1 kB
./packages/shared-chunks/observers-{hash}.js 6.8 kB / 1.6 kB 89%↑12.9 kB / 98%↑3.1 kB
./packages/shared-chunks/tags-RQ6dDyFG.js 1.9 kB / 735 B

dist/prod   0.9%↑

File Before (Size / Brotli) After (Size / Brotli)
Total (Includes all files) 1.9 MB / 455.4 kB 0.9%↑1.9 MB / 1%↑461 kB
Show files (23 files)
File Before (Size / Brotli) After (Size / Brotli)
./packages/@ember/-internals/routing/index.js 568 B / 198 B 56%↑884 B / 41%↑280 B
./packages/@ember/application/index.js 31.9 kB / 8.3 kB 1%↑32.3 kB / 1%↑8.4 kB
./packages/@ember/controller/-base.js 5.6 kB / 1.9 kB
./packages/@ember/controller/index.js 4.8 kB / 1.6 kB -18.8%↓3.9 kB / -13.8%↓1.4 kB
./packages/@ember/engine/instance.js 6.5 kB / 2 kB 9%↑7.1 kB / 10%↑2.2 kB
./packages/@ember/engine/lib/owner-proxies.js 1.6 kB / 396 B
./packages/@ember/legacy-features/index.js 1.1 kB / 473 B
./packages/@ember/object/-action.js 3.2 kB / 983 B
./packages/@ember/object/index.js 6 kB / 1.8 kB -51%↓2.9 kB / -42.2%↓1 kB
./packages/@ember/routing/index.js 15.6 kB / 3.9 kB 1%↑15.8 kB / 2%↑3.9 kB
./packages/@ember/routing/lib/qp-array.js 399 B / 213 B
./packages/@ember/routing/lib/qp-observers.js 692 B / 306 B
./packages/@ember/routing/lib/routing-service.js 4 kB / 1.2 kB -8.11%↓3.6 kB / -11.2%↓1 kB
./packages/@ember/routing/none-location.js 2.7 kB / 850 B 6%↑2.8 kB / 4%↑886 B
./packages/@ember/routing/route.js 54.8 kB / 12.1 kB 1%↑55.4 kB / 2%↑12.4 kB
./packages/@ember/routing/router-service.js 23.8 kB / 5.1 kB -1.09%↓23.6 kB / -1.19%↓5 kB
./packages/@ember/routing/router.js 44.1 kB / 10.2 kB 5%↑46.3 kB / 4%↑10.7 kB
./packages/@ember/utils/lib/classic-detect.js 459 B / 235 B
./packages/shared-chunks/chain-tags-B2J7DsxO.js 6.9 kB / 2 kB
./packages/shared-chunks/curly-{hash}.js 17.2 kB / 4.3 kB 2%↑17.5 kB / 2%↑4.4 kB
./packages/shared-chunks/evented-emitter-CYePuS74.js 3 kB / 838 B
./packages/shared-chunks/observers-{hash}.js 6.8 kB / 1.6 kB 85%↑12.6 kB / 93%↑3 kB
./packages/shared-chunks/tags-DjTuKuxi.js 1.4 kB / 555 B

smoke-tests/v2-app-template/dist   1%↑

File Before (Size / Brotli) After (Size / Brotli)
./assets/main-{hash}.js 308.4 kB / 82.8 kB 0.3%↑309.4 kB / 0.2%↑82.9 kB
./assets/modules-{hash}.js 40.3 kB / 12.1 kB 6%↑42.9 kB / 6%↑12.8 kB
Total (Includes all files) 350.3 kB / 95.6 kB 1%↑353.9 kB / 0.9%↑96.5 kB

smoke-tests/v2-app-hello-world-template/dist   0.03%↑

File Before (Size / Brotli) After (Size / Brotli)
Total (Includes all files) 132.6 kB / 37 kB 0.03%↑132.6 kB / 0.2%↑37.1 kB

smoke-tests/v2-app-modern-template/dist   ∞%↑

File Before (Size / Brotli) After (Size / Brotli)
./@embroider/virtual/app.css 116 B / 87 B
./@embroider/virtual/vendor.css
./@embroider/virtual/vendor.js 316 B / 187 B
./assets/main-C0OyZulI.js 269.2 kB / 71.5 kB
./assets/main-tn0RQdqM.css
./index.html 1.1 kB / 394 B
./robots.txt 53 B / 55 B
Total (Includes all files) 0 B / 0 B ∞%↑270.8 kB / ∞%↑72.3 kB

smoke-tests/v2-app-hello-world-template/dist-modern   ∞%↑

File Before (Size / Brotli) After (Size / Brotli)
./@embroider/virtual/vendor.css
./@embroider/virtual/vendor.js 23 B / 27 B
./assets/main-oITI6G5F.js 117.8 kB / 32.6 kB
./index.html 299 B / 126 B
Total (Includes all files) 0 B / 0 B ∞%↑118.2 kB / ∞%↑32.8 kB

🤖 This report was automatically generated by wyvox/pkg-size

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants