|
| 1 | +- Start Date: 2015-06-30 |
| 2 | +- RFC PR: https://github.com/emberjs/rfcs/pull/65 |
| 3 | +- Ember Issue: (leave this empty) |
| 4 | + |
| 5 | +# Summary |
| 6 | + |
| 7 | +Deprecations and warnings in Ember.js should have configurable runtime handlers. |
| 8 | +This allows default behavior (logging, raise when `RAISE_ON_DEPRECATION` is true) |
| 9 | +to be overridden by an enviornment (Ember's tests), addon, or other tool |
| 10 | +(like the Ember Inspector). |
| 11 | + |
| 12 | +Ember-Data and the Ember Inspector have both requested a public |
| 13 | +API for changing how deprecation and warning messages are handled. The requirements |
| 14 | +for these and other requests are complex enough that deferring the message |
| 15 | +behavior into a runtime hook is the suggested path. |
| 16 | + |
| 17 | +# Motivation |
| 18 | + |
| 19 | +`Ember.deprecate` and `Ember.warn` usually log messages. With `ENV.RAISE_ON_DEPRECATION` |
| 20 | +all deprecations will throw an exception. In some scenarios, this |
| 21 | +is less than ideal: |
| 22 | + |
| 23 | +* Ember itself needs a way to silence some deprecations before their usage |
| 24 | + is completely removed from tests. For example, many view APIs in Ember 1.13. |
| 25 | +* The Ember inspector desires to raise on specific deprecations, or silence |
| 26 | + specific deprecations. |
| 27 | +* Ember-Data also desires to silence some deprecations in tests |
| 28 | + |
| 29 | +In [PR #1141](https://github.com/emberjs/ember.js/pull/11419) |
| 30 | +a private log level API has been introduced, which allows finer grained control |
| 31 | +if specific deprecations should be logged, throwing an error or be silenced |
| 32 | +completely. For example: |
| 33 | + |
| 34 | +```js |
| 35 | +Ember.Debug._addDeprecationLevel('my-feature', Ember.Debug._deprecationLevels.LOG); |
| 36 | +// ... |
| 37 | +Ember.deprecate("x is deprecated, use Y instead", false, { id: 'my-feature' }); |
| 38 | +``` |
| 39 | + |
| 40 | +Initially a public version of this API was discussed, but it quickly became |
| 41 | +clear that a runtime hook provided more flexibility without incurring the |
| 42 | +cost of a complex log-level API. |
| 43 | + |
| 44 | +Note that "runtime" refers to Ember itself. A custom handler could be injected |
| 45 | +into Ember-CLI's template compilation code. "runtime" in this context still |
| 46 | +refers to handling deprecations raised during compilation. |
| 47 | + |
| 48 | +# Detailed design |
| 49 | + |
| 50 | +A handler for deprecations can be registered. This handler will be called |
| 51 | +with relevent information about a deprecation, including guarantees about |
| 52 | +the presence of these items: |
| 53 | + |
| 54 | +* The deprecation message |
| 55 | +* The version number where this deprecation (and feature) will be removed |
| 56 | +* The "id" of this deprecation, a stable identifier independent of the message |
| 57 | + |
| 58 | +Additionally, an application instance may be passed with the options. An example |
| 59 | +handler would look like: |
| 60 | + |
| 61 | +```js |
| 62 | +import { registerHandler } from "ember-debug/deprecations"; |
| 63 | + |
| 64 | +registerHandler(function deprecationHandler(message, options) { |
| 65 | + // * message is the deprecation message |
| 66 | + // * options.until is the version this deprecation will be removed at |
| 67 | + // * options.id is the canonical id for this deprecation |
| 68 | + if (options.until === "2.4.0") { |
| 69 | + throw new Error(message); |
| 70 | + } else { |
| 71 | + console.log(message); |
| 72 | + } |
| 73 | +}); |
| 74 | +``` |
| 75 | + |
| 76 | +Warnings are similar, but will not recieve an `until` value: |
| 77 | + |
| 78 | +```js |
| 79 | +import { registerHandler } from "ember-debug/warnings"; |
| 80 | + |
| 81 | +registerHandler(function warningHandler(message, options) { |
| 82 | + // * message is the warning message |
| 83 | + // * options.id is the canonical id for this warning |
| 84 | + if (options.id !== 'view.rerender-on-set') { |
| 85 | + console.log(message); |
| 86 | + } |
| 87 | +}); |
| 88 | +``` |
| 89 | + |
| 90 | +##### chained handlers |
| 91 | + |
| 92 | +Since several handlers may be registered, a method of deferring to a previously |
| 93 | +registered handler must be allowed. A third option is passed to handlers, the |
| 94 | +function `next` which represents the previously registered handler. |
| 95 | + |
| 96 | +For example: |
| 97 | + |
| 98 | +```js |
| 99 | +import { registerHandler } from "ember-debug/deprecations"; |
| 100 | + |
| 101 | +registerHandler(function firstDeprecationHandler(message, options, next) { |
| 102 | + console.warn(message); |
| 103 | +}); |
| 104 | + |
| 105 | +registerHandler(function secondDeprecationHandler(message, options, next) { |
| 106 | + if (options.until === "2.4.0") { |
| 107 | + throw new Error(message); |
| 108 | + } |
| 109 | + next(...arguments); |
| 110 | +}); |
| 111 | +``` |
| 112 | + |
| 113 | +The first registed handler will receive Ember's default behavior as `next`. |
| 114 | + |
| 115 | +##### new assertions for deprecate and warn |
| 116 | + |
| 117 | +Ember's APIs for deprecation and warning do not currently require any information |
| 118 | +beyond a message. It is proposed that deprecations be **required** to pass |
| 119 | +the following information: |
| 120 | + |
| 121 | +* Message |
| 122 | +* Test |
| 123 | +* Canonical id (with a format of `package-name.some-id`) |
| 124 | +* Release when this deprecation will be stripped |
| 125 | + |
| 126 | +For example: |
| 127 | + |
| 128 | +``` |
| 129 | +import Ember from "ember"; |
| 130 | +
|
| 131 | +Ember.deprecate("Some message", false, { |
| 132 | + id: 'ember-routing.query-params', |
| 133 | + until: '3.0.0' |
| 134 | +}); |
| 135 | +``` |
| 136 | + |
| 137 | +If this information is not present and assertion will be made. |
| 138 | + |
| 139 | +Warnings likewise will be required to pass a canonical id: |
| 140 | + |
| 141 | +``` |
| 142 | +import Ember from "ember"; |
| 143 | +
|
| 144 | +Ember.warn("Some warning", {id: 'ember-debug.something'}); |
| 145 | +``` |
| 146 | + |
| 147 | +##### default handlers |
| 148 | + |
| 149 | +The default handler for deprecation should be quite simple, and mirrors current |
| 150 | +behavior: |
| 151 | + |
| 152 | +```js |
| 153 | +function defaultDeprecationHandler(message, options) { |
| 154 | + if (Ember.ENV.RAISE_ON_DEPRECATION) { |
| 155 | + throw new Error(format(message, options)); |
| 156 | + } else { |
| 157 | + console.log(format(message, options)); |
| 158 | + } |
| 159 | +} |
| 160 | +``` |
| 161 | + |
| 162 | +The default handler for warnings would be simple `console.log`. |
| 163 | + |
| 164 | +# Drawbacks |
| 165 | + |
| 166 | +By not providing a robust log-level API, we are punting complexity to the |
| 167 | +consumer of this API. For a low-level tooling API such as this one, it seems |
| 168 | +and appropriate tradeoff. |
| 169 | + |
| 170 | +# Alternatives |
| 171 | + |
| 172 | +Each app can stub out `deprecate` and `warn`. |
| 173 | + |
| 174 | +# Unresolved questions |
| 175 | + |
| 176 | +`RAISE_ON_DEPRECATION` could be considered deprecated with this new API. |
0 commit comments