Repository navigation
RFC#1234 & RFC#1247: Make Service a plain class with EMBER_DROP_CLASSIC_FEATURES - #21737
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 18:48
This was referenced Oct 17, 2026
`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, `@ember/service` exports a `Service` class that extends no other class. The owner passes itself to the constructor, as it does for Glimmer components. A static `create` is the only entry point that the container needs. Without the flag, `Service` stays the same, and a cast keeps the published types. `willDestroy` stays the teardown hook. The plain class has no `destroy` method. The container calls `willDestroy`, then the `destroy` function of `@ember/destroyable`, for classes marked with `setPlainFrameworkClass`. Other instances keep the `destroy()` call. The internal router and routing services extend the exported class, so they run on the plain class in the flag build. Tests: - Three tests that used `init()` on a service now use a constructor. - The router service tests read properties without `get()`. - `set(service, ...)` replaces `service.set(...)` in one test. - The `ProxyMixin` service test skips with the flag. - A flag-only module checks the plain class. - `packages/ember/tests/without-EMBER_DROP_CLASSIC_FEATURES/service_test.js` 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-service
branch
from
October 17, 2026 20:21
cc4e81d to
b2e0f39
Compare
2 tasks
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,
b2e0f39e7e.With
EMBER_DROP_CLASSIC_FEATURES=true,Servicefrom@ember/serviceis a plain class. It extends no other class.Behavior with the flag
getOwner(this)works in the constructor.@serviceinjection works, because it only needs the owner.willDestroystays the teardown hook. The plain class has nodestroymethod. When the owner is destroyed, or onunregister, the container callswillDestroy(), thendestroy()from@ember/destroyable.isDestroyed(service)is then true, and destructors fromregisterDestructorrun.setPlainFrameworkClass, set only in the flag build. Other instances keep thedestroy()call.super.willDestroy()works, because the base has an emptywillDestroy().init,get,set,extend,reopen,destroy,isDestroyingandisDestroyeddo not exist on the class.createstays, because the container creates every instance through it.Without the flag,
Servicedoes not change. A cast keeps the published type as the classic class, so apps get no new type errors.Internal services
RouterServiceand the privateRoutingServiceextend the exported class, so they run on the plain class in the flag build. They needed no source changes. ThewillDestroyhook ofRouterServiceruns in both builds.Tests
@ember/service/tests/drop_classic_features_test.jswillDestroyhook. Registers only with the flag.ember/tests/without-EMBER_DROP_CLASSIC_FEATURES/service_test.jsEmberObjectbase,init,Service.extend, properties fromcreate,get/set,willDestroy, the destroy flags, andget()on the router service. Registers only without the flag.init()toconstructor(owner)in three testsservice.get('x')toservice.xin three router service test filesserviceInstance.set(...)toset(serviceInstance, ...)ProxyMixinservice test skips with the flagService.extendChecks
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