Skip to content

Latest commit

Β 

History

History
417 lines (336 loc) Β· 29.5 KB

1003-deprecation-import-ember-from-ember.md

File metadata and controls

417 lines (336 loc) Β· 29.5 KB
stage start-date release-date release-versions teams prs project-link
ready-for-release
2024-01-22 00:00:00 UTC
cli
data
framework
learning
steering
typescript
accepted ready-for-release

Deprecate `import Ember from 'ember';

Summary

This RFC proprosing deprecating all APIs that have module-based replacements, as described in RFC #176 as well as other Ember.* apis that are no longer needed.

Motivation

The import Ember from 'ember'; set of APIs is implementn as a barrel file, and properly optimizing barrel files is a lot of work, requiring integration with build time tools.

If anyone one dependency in an app's dependency tree does import ... from 'ember', every feature of the framework is shipped to your users, without any ability for you to optimize.

By removing this set of exports, we have an opportunity to shrink some apps (as some APIs are not used), improving the load performance of ember apps -- and we removing all of these gives us a chance to have a better grasp of what we can get rid of permananently.

Many of these APIs already have alternatives, and those will be called out explicitly in the Transition Path below.

Transition Path

This list is semi-exhaustive, in that it covers every export from 'ember', but may not exhaustivily provide alternatives.

Throughout the rest of this RFC, the following key will be used:

  • 🌐 to mean "this is public API"
  • πŸ”’ to mean "this is private API"
  • 🧷 to mean "this is protected API"
  • 🫣 to mean "no declared access"

Testing utilities

APIs for wiring up a test framework (e.g. QUnit, etc)

API Usage: EmberObserver --
🌐 Ember.Test A good few
🌐 Ember.Test.Adapter ember-cli-fastboot and @ember/test-helpers currently available at @ember/test
🌐 Ember.Test.QUnitAdapter ember-cli-fastboot
🌐 Ember.setupForTesting ember-cli-fastboot

If needed, these will need to be moved to a module such as @ember/test.

A way to communicate with the ember-inspector

The inspector will be hit especially hard by the removal of these APIs.

A good few already have available imports though.

API import
πŸ”’ Ember.meta import { meta } from '@ember/-internals/meta';
🌐 Ember.VERSION import { VERSION } from '@ember/version';
πŸ”’ Ember._captureRenderTree import { captureRenderTree } from '@ember/debug';
πŸ”’ Ember.instrument import { instrument } from '@ember/instrumentation';
πŸ”’ Ember.subscribe import { subscribe } from '@ember/instrumentation';
πŸ”’ Ember.Instrumentation.* import { * } from '@ember/instrumentation';
🫣 Ember.ViewUtils import * as viewUtils from '@ember/-internals/views';1
πŸ”’ Ember.ViewUtils.getChildViews import { getChildViews } from '@ember/-internals/views';
🫣 Ember.ViewUtils.getElementView import { getElementView } from '@ember/-internals/views';
πŸ”’ Ember.ViewUtils.getRootViews import { getRootViews } from '@ember/-internals/views';
πŸ”’ Ember.ViewUtils.getViewBounds import { getViewBounds } from '@ember/-internals/views';
πŸ”’ Ember.ViewUtils.getViewBoundingClientRect import { getViewBoundingClientRect } from '@ember/-internals/views';
πŸ”’ Ember.ViewUtils.getViewClientRects import { getViewClientRects } from '@ember/-internals/views';
πŸ”’ Ember.ViewUtils.getViewElement import { getViewElement } from '@ember/-internals/views';
🫣 Ember.ViewUtils.isSimpleClick import { isSimpleClick } from '@ember/-internals/views';
🫣 Ember.ViewUtils.isSerializationFirstNode import { isSerializationFirstNode } from '@ember/-internals/glimmer';

Perhaps we can have folks add this to their apps:

import { macroCondition, isDevelopingApp, importSync } from '@embroider/macros';

if (macroCondition(isDevelopingApp())) {
  // maybe this is side-effecting and installs 
  // some functions on `globalThis` that the inspector could call
  // since the inspector can't import modules from a built app.
  importSync('@ember/inspector-support');
}

No replacements.

Applies to both the value and type exports (if applicable). All of these will not be re-exported from other @ember/* packages, but the following tables will show addon usage2 in the ecosystem and potential paths forward for library authors.

API Usage: EmberObserver Migration
🫣 Ember._getPath None n/a
🫣 Ember.isNamespace None n/a
🫣 Ember.toString None n/a
πŸ”’ Ember.Container Many, but old or docs n/a
πŸ”’ Ember.Registry Many, but old or docs n/a

Internal decorator utils

API Usage: EmberObserver Migration
🫣 Ember._descriptor EmberObserver: None n/a
πŸ”’ Ember._setClassicDecorator EmberObserver: ember-concurrency n/a

Reactivity

API Usage: EmberObserver Migration
πŸ”’ Ember.beginPropertyChanges ember-m3 + old addons n/a
πŸ”’ Ember.endPropertyChanges ember-m3 + old addons n/a
πŸ”’ Ember.changeProperties None n/a

Observable

API Usage: EmberObserver Migration
🌐 Ember.hasListeners None n/a

Mixins

API Usage: EmberObserver Migration
πŸ”’ Ember._ContainerProxyMixin mostly old addons. Includes ember-decorators, ember-data-has-many-query, ember-graphql-adapter, ember-cli-fastboot (in tests / test-support) n/a
πŸ”’ Ember._RegistryProxyMixin mostly old addons. Includes ember-decorators, ember-data-has-many-query, ember-graphql-adapter, ember-cli-fastboot (in tests / test-support) n/a
πŸ”’ Ember._ProxyMixin ember-bootstrap-components, 8 years ago n/a
πŸ”’ Ember.ActionHandler 'ember-error-tracker' + old addons. Many usages include pre-modules Ember usage. n/a
πŸ”’ Ember.Comparable ember-data-model-fragments n/a

Utility

API Usage: EmberObserver Migration
🫣 Ember.lookup old addons, > 6 years Use getOwner(...).lookup from @ember/owner
🌐 Ember.libraries Many usages, mostly ember-data and related This isn't a behavior that Ember needs to provide, nor should it be library authors' responsibilty to register themselves with a library listing system. App authors could choose to use any webpack or other build plugin that collections this information, such as webpack-node-modules-list or unplugin-info.
🫣 Ember._Cache None n/a
πŸ”’ Ember.GUID_KEY ember-data-save-relationships, 6 years ago n/a
πŸ”’ Ember.canInvoke @summit-electric-supply use optional chaining, e.g.: this.foo?.method?.()
πŸ”’ Ember.generateGuid `ember-flexberry + old addons Use guidFor or uuid or the browser-native crypto.randomUUID()
🌐 Ember.uuid 3 recent addons Use guidFor or uuid or the browser-native crypto.randomUUID()
πŸ”’ Ember.wrap None n/a
πŸ”’ Ember.inspect old addons n/a
🫣 Ember.Debug old addons use @ember/debug
🫣 Ember.cacheFor old addons potentially @glimmer/tracking/primitives/cache
🌐 Ember.ComputedProperty aside from docs, old addons. Most recent usage is 3 years ago in ember-cli-furnance-validation n/a
🫣 Ember.RouterDSL old addons n/a
πŸ”’ Ember.controllerFor None n/a
πŸ”’ Ember.generateController bitbird-core-ember-routing, 5 years ago n/a
πŸ”’ Ember.generateControllerFactory None n/a
API Usage: EmberObserver Migration
🌐 Ember.VERSION EmberObserver: Not many usages. This has the ember version in it, but it could be converted to a virtual module to import from somewhere, such as @ember/version
πŸ”’ Ember._Backburner EmberObserver: None n/a
🌐 Ember.inject EmberObserver: Many, all using classic classes. A lot of results are also classic-class docs. Use @service

Any projects using these are already not safe for embroider and won't work with Vite

API Usage: EmberObserver Migration
🫣 Ember.__loader 159 addons. Some experimental. Most from @ascua n/a
🫣 Ember.__loader.require same as Ember.__loader n/a
🫣 Ember.__loader.define 5 addons, ~2 recent. One is ember-cli-fastboot (tests, test-support). n/a
🫣 Ember.__loader.registry 13 addons, ~5 recent. One is ember-cli-fastboot (tests, test-support). n/a
πŸ”’ Ember.BOOTED None n/a

Replaced by RFC #931. For scenarios where folks would like to compile templates at runtime, see RFC #931 or the code of ember-repl.

API Usage: EmberObserver Migration
πŸ”’ Ember.TEMPLATES ember-resolver n/a
🫣 Ember.HTMLBars Lots of usage (encompasses the below APIs) n/a
🫣 Ember.HTMLBars.DOMHelper ember-cli-fastboot uses protocolForURL, parseHTML n/a
🫣 Ember.HTMLBars.template ember-cli-fastboot (and ember-ast-hot-load) n/a
🫣 Ember.HTMLBars.compile old addons n/a
🫣 Ember.HTMLBars.precomple ember-ast-hot-load n/a
🫣 Ember.Handlebars 174 addons, mostly @ascua and also a lot of mentions in docs. n/a
🫣 Ember.Handlebars.template None n/a
🫣 Ember.Handlebars.Utils.escapeExpression 100 addons, mostly @ascua Removed in ember.js PR#20360 as it is not public API.
🫣 Ember.Handlebars.compile ember-cli-fastboot and ember-collection n/a
🫣 Ember.Handlebars.precomple None n/a

Other APIs

  • 🫣 Ember.testing
    Instead, use

    import { macroCondition, isTesting } from '@embroider/macros';
    
    // ...
    
    if (macroCondition(isTesting())) {
      // test only code here
    }
  • 🌐 Ember.onerror Instead you may be able to use an event listener for the error event on window.

    window.addEventListener('error', /* ... event handler ... */);

    For promise rejections, you'll want to use the unhandledrejection event.

    window.addEventListener('unhandledrejection', /* ... event handler ... */);

    If you really need the original behavior:

    import { getOnerror, setOnerror } from '@ember/-internals/error-handling';

    But this should not be needed.

Imports Available

Most of this is covered in RFC #176

Unless otherwise stated, there will not be usage-based decision on these, as they all exist under available imports today.

Ember. API Use this instead
🌐 Ember.FEATURES import { isEnabled, FEATURES } from '@ember/canary-features';
🌐 Ember._setComponentManager import { setComponentManager } from '@ember/component';
🌐 Ember._componentManagerCapabilities import { capabilities } from '@ember/component';
🌐 Ember._modifierManagerCapabilities import { capabilities } from '@ember/modifier';
🌐 Ember._createCache import { createCache } from '@glimmer/tracking/primitives/cache'; RFC #615
🌐 Ember._cacheGetValue import { getValue } from '@glimmer/tracking/primitives/cache'; RFC #615
🌐 Ember._cacheIsConst import { isConst } from '@glimmer/tracking/primitives/cache'; RFC #615
🌐 Ember._tracked import { tracked } from '@glimmer/tracking';
🌐 Ember.RSVP import RSVP from 'rsvp';
🌐 Ember.guidFor import { guidFor } from '@ember/object/internals';
🌐 Ember.getOwner import { getOwner } from '@ember/owner';
🌐 Ember.setOwner import { setOwner } from '@ember/owner';
🌐 Ember.onLoad import { onLoad } from '@ember/application';
🌐 Ember.runLoadHooks import { runLoadHooks } from '@ember/application';
🌐 Ember.Application import Application from '@ember/application';
🌐 Ember.ApplicationInstance import ApplicationInstance from '@ember/application/instance';
🌐 Ember.Namespace import Namespace from '@ember/application/namespace';
🌐 Ember.A import { A } from '@ember/array';
🌐 Ember.Array import Array from '@ember/array';
🌐 Ember.NativeArray import { NativeArray } from '@ember/array';
🌐 Ember.isArray import { isArray } from '@ember/array';
πŸ”’ Ember.makeArray import { makeArray } from '@ember/array';
🌐 Ember.MutableArray import MutableArray from '@ember/array/mutable';
🌐 Ember.ArrayProxy import ArrayProxy from '@ember/array/proxy';
🌐 Ember._Input import { Input } from '@ember/component';
🌐 Ember.Component import Component from '@ember/component';
🌐 Ember.Helper import Helper from '@ember/component/helper';
🌐 Ember.Controller import Controller from '@ember/controller';
πŸ”’ Ember.ControllerMixin import { ControllerMixin } from '@ember/controller';
🌐 Ember.assert import { assert } from '@ember/debug';
🌐 Ember.warn import { warn } from '@ember/debug';
🌐 Ember.debug import { debug } from '@ember/debug';
🌐 Ember.deprecate import { deprecate } from '@ember/debug';
🫣 Ember.deprecateFunc import { deprecateFunc } from '@ember/debug';
🌐 Ember.runInDebug import { runInDebug } from '@ember/debug';
🌐 Ember.Debug.registerDeprecationHandler import { registerDeprecationHandler } from '@ember/debug';
🌐 Ember.ContainerDebugAdapter import ContainerDebugAdapter from '@ember/debug/container-debug-adapter';
🌐 Ember.DataAdapter import DataAdapter from '@ember/debug/data-adapter';
🌐 Ember._assertDestroyablesDestroyed import { assertDestroyablesDestroyed } from '@ember/destroyable';
🌐 Ember._associateDestroyableChild import { associateDestroyableChild } from '@ember/destroyable';
🌐 Ember._enableDestroyableTracking import { enableDestroyableTracking } from '@ember/destroyable';
🌐 Ember._isDestroying import { isDestroying } from '@ember/destroyable';
🌐 Ember._isDestroyed import { isDestroyed } from '@ember/destroyable';
🌐 Ember._registerDestructor import { registerDestructor } from '@ember/destroyable';
🌐 Ember._unregisterDestructor import { unregisterDestructor } from '@ember/destroyable';
🌐 Ember.destroy import { destroy } from '@ember/destroyable';
🌐 Ember.Engine import Engine from '@ember/engine';
🌐 Ember.EngineInstance import Engine from '@ember/engine/instance';
πŸ”’ Ember.Enumerable import Enumerable from '@ember/enumerable';
πŸ”’ Ember.MutableEnumerable import MutableEnumerable from '@ember/enumerable/mutable';
🌐 Ember.Object import Object from '@ember/object';
🌐 Ember._action import { action } from '@ember/object';
🌐 Ember.computed import { computed } from '@ember/object';
🌐 Ember.defineProperty import { defineProperty } from '@ember/object';
🌐 Ember.get import { get } from '@ember/object';
🌐 Ember.getProperties import { getProperties } from '@ember/object';
🌐 Ember.notifyPropertyChange import { notifyPropertyChange } from '@ember/object';
🌐 Ember.observer import { observer } from '@ember/object';
🌐 Ember.set import { set } from '@ember/object';
🌐 Ember.trySet import { trySet } from '@ember/object';
🌐 Ember.setProperties import { setProperties } from '@ember/object';
🌐 Ember._dependentKeyCompat import { dependentKeyCompat } from '@ember/object/compat';
🌐 Ember.expandProperties import { expandProperties } from '@ember/object/computed';
🌐 Ember.CoreObject import EmberObject from '@ember/object';
🌐 Ember.Evented import Evented from '@ember/object/evented';
🌐 Ember.on import { on } from '@ember/object/evented';
🌐 Ember.addListener import { addListener } from '@ember/object/events';
🌐 Ember.removeListener import { removeListener } from '@ember/object/events';
🌐 Ember.sendEvent import { sendEvent } from '@ember/object/events';
🌐 Ember.Mixin import Mixin from '@ember/object/mixin';
πŸ”’ Ember.mixin import { mixin } from '@ember/object/mixin';
🌐 Ember.Observable import Observable from '@ember/object/observable';
🌐 Ember.addObserver import { addObserver } from '@ember/object/observers';
🌐 Ember.removeObserver import { removeObserver } from '@ember/object/observers';
🌐 Ember.PromiseProxyMixin import EmberPromiseProxyMixin from '@ember/object/promise-proxy-mixin';
🌐 Ember.ObjectProxy import ObjectProxy from '@ember/object/proxy';
🧷 Ember.HistoryLocation import HistoryLocation from '@ember/routing/history-location';
🧷 Ember.HashLocation import HashLocation from '@ember/routing/hash-location';
🧷 Ember.NoneLocation import NoneLocation from '@ember/routing/none-location';
🌐 Ember.Route import Route from '@ember/routing/route';
🌐 Ember.run import { run } from '@ember/runloop';
🌐 Ember.Service import Service from '@ember/service';
🌐 Ember.compare import { compare } from '@ember/utils';
🌐 Ember.isBlank import { isBlank } from '@ember/utils';
🌐 Ember.isEmpty import { isEmpty } from '@ember/utils';
🌐 Ember.isEqual import { isEqual } from '@ember/utils';
🌐 Ember.isPresent import { isPresent } from '@ember/utils';
🌐 Ember.typeOf import { typeOf } from '@ember/utils';
🌐 Ember._getComponentTemplate import { getComponentTemplate } from '@ember/component';
🌐 Ember._setComponentTemplate import { setComponentTemplate } from '@ember/component';
🌐 Ember._helperManagerCapabilities import { capabilities } from '@ember/helper';
🌐 Ember._setHelperManager import { setHelperManager } from '@ember/helper';
🌐 Ember._setModifierManager import { setModifierManager } from '@ember/modifier';
🌐 Ember._templateOnlyComponent import templateOnly from '@ember/component/template-only';
🌐 Ember._invokeHelper import { invokeHelper } from '@ember/helper';
🌐 Ember._hash import { hash } from '@ember/helper';
🌐 Ember._array import { array } from '@ember/helper';
🌐 Ember._concat import { concat } from '@ember/helper';
🌐 Ember._get import { get } from '@ember/helper';
🌐 Ember._on import { on } from '@ember/modifier';
🌐 Ember._fn import { fn } from '@ember/helper';
🌐 Ember.ENV import MyEnv from '<my-app>/config/environment'; (for apps) or owner.resolveRegistration('config:environment') for addons

Implementation Plan

These can happen in any order

  • Add deprecations to each Ember.* access
  • Add the Testing utilities to @ember/test, if needed.
  • Add an @ember/version package to ember-source
  • Add re-exports of private APIs, ComputedProperty, and _setClassicDecorator These will still be deprecated on Ember., and will be deprecated themselves as we progress through deprecating Ember Classic.
  • Update ember-inspector to use imports for the internals and instrumentation APIs
  • Add @ember/inspector-support to ember-source to manage things like LIBRARIES.
    import { libraries } from '@ember/inspector-support';
    
    libraries.add('ember-data', '5.3.1');
    // and/or
    libraries.addAll(depInfoFromPlugin);
  • Add deprecation guide entries for each API

How We Teach This

While @ember/-internals were created to be internal, introducing new names for them would create churn and would make it harder for addon authors to support a wide range of versions. The internals paths all work today on supported releases, so dropping the deprecated usage doesn't reduce your support matrix, whereas using a newly-introduced import path would.

All @ember/-internals(/*)? APIs mentioned above are now public API, and to remove any of those APIs, they will need to go through the deprecation process.


The guides already use the modern imports where available.

There is a place that needs updating, around advanced debugging, where folks configure Backburner to be in debug mode.

When using embroider and staticEmberSource: true, the benefits of not having this file can be realized in apps (as long as the app and all consumed addons do not import from 'ember')

Available Codemods

Deprecation Guide

  • Separate ids for each API so that folks don't have to scroll too far to get to their migration path (if a migration path exists).
  • Mostly using the above tables, but without the Usage: EmberObserver column.

Drawbacks

n/a, to be more module-friendly, we must get rid of the 'ember' import.

Alternatives

Don't use @ember/-internals and create new public APIs for all of the things currently under @ember/-internals. This would create a lot of churn in the ecosystem, when we want to get rid of some of these APIs anyway.

Unresolved questions

n/a

Q: Do our instrumentation and internals sub-packages have any SemVer guarantees? Or are we allowed to "do what we need to" and not care about public-facing SemVer? A: If something is privately but heavily used, we will try to deprecate before removing the API and make sure the deprecation makes it in to an LTS before that removal.

Footnotes

  1. Not all of these exports are used for ViewUtils. ↩

  2. Addons are notorious for doing things they shouldn't, accessing private APIs, doing crazy things so users don't have to, working around ecosystem and broader ecosystem problems etc. It's also expected that addon authors will be able to handle migrations more quickly than app devs. ↩