Repository navigation
RFC#1234 & RFC#1247: Base Route on a plain class with EMBER_DROP_CLASSIC_FEATURES - #21739
Draft
NullVoxPopuli-ai-agent wants to merge 2 commits into
Draft
NullVoxPopuli-ai-agent wants to merge 2 commits into
NullVoxPopuli-ai-agent wants to merge 2 commits into
Conversation
NullVoxPopuli
marked this pull request as draft
October 17, 2026 19:56
`import.meta.env?.EMBER_DROP_CLASSIC_FEATURES` is a build-time flag for RFC 1247. Later changes read it where a classic feature is defined, and remove that feature from builds that set the flag. - `ImportMetaEnv` declares the flag for the type checks. - `EMBER_DROP_` is in the Vite `envPrefix`, so `EMBER_DROP_CLASSIC_FEATURES=true pnpm vite build` sets it for the tests. - CI has a "Classic features dropped" variant that builds and tests with the flag. - The hello-world smoke test sets the flag with a Vite `define` entry, so the package-size workflow measures an app without the classic features. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With the flag, `Route` from `@ember/routing/route` extends a private plain base class, not `EmberObject` and the `ActionHandler` mixin. The class body is the same in both modes. The base sets the owner in the constructor and keeps a static `create` for the container. A route without an owner is allowed, as before. `willDestroy` stays the teardown hook. The base has a `destroy()` method that calls the `destroy` function of `@glimmer/destroyable`, and an empty `willDestroy()`. Only a subclass that overrides `willDestroy` gets a destructor for it. `Route` overrides it to call `teardownViews`, so that runs with the flag too. The defaults that `Route.reopen` set (`queryParams`, `templateName`, `controllerName`, `send` and `actions`) go on the prototype with the flag. Without the flag, `reopen` still sets them and merges `queryParams`. `send` reads the destroy state through `@glimmer/destroyable`, so it works on both bases. A cast keeps the published types. Tests: - Route subclasses in tests use a constructor, not `init()`, and `super`, not `this._super`. - The route unit tests set properties after `create()`, read with `get()`, and destroy with the `destroy` function. - The test of the `queryParams` array assertion skips with the flag. The assertion comes from the merge of `Route.extend()`. - A new flag-only module checks the plain base. - A new suite in `packages/ember/tests/without-EMBER_DROP_CLASSIC_FEATURES` checks the classic API in builds without the flag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
NullVoxPopuli-ai-agent
force-pushed
the
nvp/plain-route
branch
from
October 17, 2026 20:27
6da5dda to
ccc024b
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Merge #21726 first. This PR sits on top of it. Review only the last commit,
ccc024be55.With
EMBER_DROP_CLASSIC_FEATURES=true,Routefrom@ember/routing/routeno longer extendsEmberObjector theActionHandlermixin. It extends a private plain base class, which extends no other class.Behavior with the flag
getOwner(this)and@servicework in the constructor.queryParams,@actionhandlers andsendwork as before.willDestroy()stays the teardown hook. It runs once, when the owner is destroyed or whenroute.destroy()is called.isDestroyed(route)is then true.init,get,set,extend,reopen,isDestroyingandisDestroyeddo not exist on the class.createstays, because the container creates every instance through it. A route without an owner is allowed, as on the classic class.Without the flag,
Routedoes not change. A cast keeps the published type as the classic class, so apps get no new type errors.Design
The class body is the same in both modes. Only the base class changes with the flag. Two parts of the classic class needed a plain equivalent:
Route.reopen({...})setsqueryParams,templateName,controllerName,sendandactionsRoute.prototypesendasserts onthis.isDestroyingandthis.isDestroyedsendusesisDestroying()andisDestroyed()from@glimmer/destroyable, in both modesThe
@computedgetters_qpand_storeand theget()calls in the class body work on plain objects, so they did not change.The flag build does not merge
queryParamsacross subclasses. The merge comes fromRoute.extend(), which does not exist with the flag. A native subclass field replaces thequeryParamsof its parent in both modes.The plain base has a
destroy()method, which calls thedestroyfunction of@glimmer/destroyable, and an emptywillDestroy()method. If an instance overrideswillDestroy, the constructor registers it as a destructor.RouteoverrideswillDestroyto tear down its views, so that teardown also runs in the flag build.Tests
@ember/routing/tests/system/route_drop_classic_features_test.jsEmberObject, owner in the constructor,willDestroyon both destroy paths, the view teardown ofRoute, and a route with a model hook and query params in an application. Registers only with the flag.ember/tests/without-EMBER_DROP_CLASSIC_FEATURES/route_test.jsEmberObjectbase,init, properties fromcreate, thequeryParamsmerge ofRoute.extend,get/set,willDestroy, the destroy flags, andthis._superin a hook. Registers only without the flag.init()toconstructor(owner)in Route subclasses of six test filesthis._super(...arguments)tosuper.setupController(...arguments)create(), read withget(), and destroy with thedestroyfunctionqueryParamsarray assertion skips with the flagRoute.extend()Checks
ALL_DEPRECATIONS_ENABLED=trueOVERRIDE_DEPRECATION_VERSION=15.0.0EMBER_DROP_CLASSIC_FEATURES=trueThe type checks of the internals and the published types pass, with and without stable decorators.
eslint,prettierand the docs check pass.🤖 Generated with Claude Code