From d147055d32802e6e79f71cba1af567b8f3e7a76d Mon Sep 17 00:00:00 2001 From: Chris Garrett Date: Wed, 6 Feb 2019 17:13:18 -0800 Subject: [PATCH 1/8] Decorator Support RFC --- text/0000-decorator-support.md | 139 +++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 text/0000-decorator-support.md diff --git a/text/0000-decorator-support.md b/text/0000-decorator-support.md new file mode 100644 index 0000000000..2cd9d768a3 --- /dev/null +++ b/text/0000-decorator-support.md @@ -0,0 +1,139 @@ +- Start Date: 2019-02-06 +- Relevant Team(s): Ember.js, Ember Data, Ember CLI, Learning, Steering +- RFC PR: +- Tracking: (leave this empty) + +# Decorator Support + +## Summary + +Move forward with the Decorators RFC, and support decorators while they remain +in stage 2 and move forward through the TC39 process. + +## Motivation + +The recently merged [Decorators +RFC](https://github.com/emberjs/rfcs/blob/master/text/0408-decorators.md) +contained an explicit condition: That it was premised on decorators moving from +stage 2 in the TC39 process to stage 3. If this were to not happen, the original +RFC stipulates that a followup (this RFC) should be created to clarify whether +or not we should move forward with decorators while they remain in stage 2, and +how we would do so. + +This RFC proposes that we do so. As discussed in the original, the decorator +pattern has been in use in Ember since the beginning, and there is no easy path +forward to native class syntax without a native equivalent to "classic" +decorators. + +[Stage 2 in TC39](https://tc39.github.io/process-document/) signals that the +committee expects this feature to be included in the language in the future, and +is working out the details and semantics. The fact that decorators _remain_ in +stage 2, and have not been rejected, signals that they still expect this to be +the case. This is also the feedback that the core team received in the January +meeting. _Crucially_, all parties were in agreement about the _invocation_ +syntax of decorators: + +```js +class Person { + @decorate name; +} +``` + +This is the most stable part of the spec, and the one that average developers +will come into contact with most. It means that changes to the spec will likely +affect library and framework maintainers, but **not** end users. This places the +burden of dealing with the spec changes on the Ember.js core team, and addon +authors who wish to adopt decorators while they are in stage 2. + +As such, adopting decorators now should present a minimal amount of risk to +Ember and its users, and while it is not ideal, it allows us to continue moving +forward and innovating as a framework. + +## Detailed design + +### Classic Classes + +The first thing to note is that Ember.js will continue to work with classic +classes and classic class syntax for the forseeable future, as a way for users +to opt-out if they don't want to take the risk of using native classes. This +includes: + +- Classic Classes +- Classic Components +- Mixins +- Computed Properties +- Observers +- Tracked Properties +- Injections +- All existing classes defined with classic class syntax + +Notably, `GlimmerComponent` will _not_ support classic class syntax, due to its +constraint of having to support both Ember.js _and_ Glimmer.js, where the +classic class model is not available. However, creating an API compatible +classic class version using Ember's component manager API should be possible if +users want to write Glimmer-like component's with classic syntax. + +### Decorator Versioning and Support + +Because the decorators spec has changed while remaining in a stage 2, it's no +longer enough to refer to "stage 2" as a version. Since Babel will be providing +the transforms, we will use their versioning system. [This is currently in +development](https://github.com/babel/babel/pull/9360), but looks like it will +be based on the TC39 meeting that the spec was presented at. For instance, the +current transforms available in Babel are referred to as `nov-2018`. + +Ember will begin by supporting the _latest_ version of the decorators transform +provided by Babel. Currently this is the `nov-2018` version, but by the time +decorators are released this may change. + +The decorators version will be configurable on a _per-app/engine/addon_ basis. +This will allow the ecosystem to adopt decorators without fears that if an +application updates, it could break their decorators. If no version is +specified, the transform will default to the latest version supported by Ember +at that time. In many cases the only decorators in use will be Ember's, so this +allows the ecosystem to upgrade without additional maintenance costs. + +When new transforms are released, Ember will continue to support older versions +of the transforms alongside the new ones until _at least_ the next LTS release, +with deprecation warnings for any users of the older transforms. This will mean +shipping multiple different versions of decorators at once, and providing a way +for apps/addons to import the correct version. Additionally, Ember will attempt +to provide addon authors with tools that allow _them_ to do this as well, so +they can support multiple versions of transforms easily and transparently. The +exact API for these tools is out of scope for this RFC, and will likely be +dependent on their implementation. + +## How we teach this + +As mentioned in the motivation section, the _invocation_ side of decorators +seems to be much more stable currently, so we ideally will not have to teach end +consumers too much about the details of this system. We should definitely +provide a disclaimer that outlines how our support structure works, and how +users can lock their version if they are using custom decorators. This should be +included _early_ in the guides. + +We should also provide thorough documentation for any tooling provided to help +addon authors write their own decorators. This will be a bit more of a moving +target, since this RFC does not define exact APIs and they will not be a +priority until Ember itself prepares to support multiple versions of the +transforms. + +## Drawbacks + +The fact that decorators did not move to stage 3 signals that there may be +additional changes to the spec in the near future. Adopting them now could cause +more churn than it's worth. + +## Alternatives + +- We could continue to rely on unofficial addons such as `ember-decorators`, + which allow users to opt-in to using decorators. However, these libaries have + limitations that first class decorators will not have, and they don't allow us + to update the official guides to use them. + +- We could create an official decorators addon. However, this means that + decorators would be available at a different import path, meaning that any + code which seeks to work with _both_ classic classes and native classes would + have to be written twice. This would be very difficult for applications that + are mid-transition, and even more difficult throughout the ecosystem for addon + authors. From 11096d6cec2c90091d5f867105cc34d3ac25b8f5 Mon Sep 17 00:00:00 2001 From: Chris Garrett Date: Wed, 6 Feb 2019 18:42:23 -0800 Subject: [PATCH 2/8] updates based on feedback --- text/0000-decorator-support.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/text/0000-decorator-support.md b/text/0000-decorator-support.md index 2cd9d768a3..e3bd0da1f5 100644 --- a/text/0000-decorator-support.md +++ b/text/0000-decorator-support.md @@ -30,7 +30,7 @@ committee expects this feature to be included in the language in the future, and is working out the details and semantics. The fact that decorators _remain_ in stage 2, and have not been rejected, signals that they still expect this to be the case. This is also the feedback that the core team received in the January -meeting. _Crucially_, all parties were in agreement about the _invocation_ +TC39 meeting. _Crucially_, all parties were in agreement about the _invocation_ syntax of decorators: ```js @@ -41,7 +41,8 @@ class Person { This is the most stable part of the spec, and the one that average developers will come into contact with most. It means that changes to the spec will likely -affect library and framework maintainers, but **not** end users. This places the +affect the _application_ of decorators, which will in turn affect library and +framework maintainers, but **not** end users in most cases. This places the burden of dealing with the spec changes on the Ember.js core team, and addon authors who wish to adopt decorators while they are in stage 2. @@ -91,7 +92,9 @@ This will allow the ecosystem to adopt decorators without fears that if an application updates, it could break their decorators. If no version is specified, the transform will default to the latest version supported by Ember at that time. In many cases the only decorators in use will be Ember's, so this -allows the ecosystem to upgrade without additional maintenance costs. +allows the ecosystem to upgrade without additional maintenance costs. The exact +method for specifying the version will be determined during implementation, but +will likely be a configuration option or addon. When new transforms are released, Ember will continue to support older versions of the transforms alongside the new ones until _at least_ the next LTS release, From a4567691aae1aba0e085599432864c62cb761e67 Mon Sep 17 00:00:00 2001 From: Chris Garrett Date: Thu, 7 Feb 2019 07:50:15 -0800 Subject: [PATCH 3/8] Add PR link --- text/0000-decorator-support.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-decorator-support.md b/text/0000-decorator-support.md index e3bd0da1f5..98175a513a 100644 --- a/text/0000-decorator-support.md +++ b/text/0000-decorator-support.md @@ -1,6 +1,6 @@ - Start Date: 2019-02-06 - Relevant Team(s): Ember.js, Ember Data, Ember CLI, Learning, Steering -- RFC PR: +- RFC PR: https://github.com/emberjs/rfcs/pull/440 - Tracking: (leave this empty) # Decorator Support From 72a21680b05a07384a166ecd6ff9fddb2c5ef711 Mon Sep 17 00:00:00 2001 From: Mike North Date: Fri, 8 Feb 2019 13:43:38 -0800 Subject: [PATCH 4/8] Update text/0000-decorator-support.md Co-Authored-By: pzuraq --- text/0000-decorator-support.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-decorator-support.md b/text/0000-decorator-support.md index 98175a513a..84adea170e 100644 --- a/text/0000-decorator-support.md +++ b/text/0000-decorator-support.md @@ -46,7 +46,7 @@ framework maintainers, but **not** end users in most cases. This places the burden of dealing with the spec changes on the Ember.js core team, and addon authors who wish to adopt decorators while they are in stage 2. -As such, adopting decorators now should present a minimal amount of risk to +As such, adopting framework-provided decorators now should present a minimal amount of risk to Ember and its users, and while it is not ideal, it allows us to continue moving forward and innovating as a framework. From bf7c70ecc8940de584f0ca0698ca8d469fa25b31 Mon Sep 17 00:00:00 2001 From: Chris Garrett Date: Fri, 8 Feb 2019 14:31:42 -0800 Subject: [PATCH 5/8] add constraints list --- text/0000-decorator-support.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/text/0000-decorator-support.md b/text/0000-decorator-support.md index 84adea170e..6c91afa801 100644 --- a/text/0000-decorator-support.md +++ b/text/0000-decorator-support.md @@ -76,6 +76,28 @@ users want to write Glimmer-like component's with classic syntax. ### Decorator Versioning and Support +The list of constraints which we are optimizing for is as follows, from the +highest priority to the lowest priority: + +- Aligning with the decorator spec as it evolves over time. +- Ensuring the maintenance cost for end users of _Ember's framework-provided_ + decorators is as small as reasonably possible, accounting for changes in the + spec. +- Ensuring addons that make use of supported versions of decorators can be + consumed in all apps that have decorator support, even if decorator versions + mismatch. +- Minimizing the maintenance cost for end users of _addon provided decorators_ + as the spec evolves. +- Minimizing the increase to static asset sizes, resulting from adding decorator + support. + +Non-goals/non-constraints: + +- Interop with older versions of the spec, including Typescript's current + transforms. +- Supporting all future versions of the spec simultaneously throughout the + ecosystem. + Because the decorators spec has changed while remaining in a stage 2, it's no longer enough to refer to "stage 2" as a version. Since Babel will be providing the transforms, we will use their versioning system. [This is currently in From 570f186a7914783fcd3e0af0e71a0e6537a4dcb0 Mon Sep 17 00:00:00 2001 From: Chris Garrett Date: Sat, 2 Mar 2019 08:33:58 -0800 Subject: [PATCH 6/8] update to support stage 1 --- text/0000-decorator-support.md | 251 +++++++++++++++++++++------------ 1 file changed, 159 insertions(+), 92 deletions(-) diff --git a/text/0000-decorator-support.md b/text/0000-decorator-support.md index 6c91afa801..aba1b67fa6 100644 --- a/text/0000-decorator-support.md +++ b/text/0000-decorator-support.md @@ -7,8 +7,8 @@ ## Summary -Move forward with the Decorators RFC, and support decorators while they remain -in stage 2 and move forward through the TC39 process. +Move forward with the Decorators RFC via stage 1 decorators, and await a stable +stage 3+ proposal. ## Motivation @@ -29,26 +29,25 @@ decorators. committee expects this feature to be included in the language in the future, and is working out the details and semantics. The fact that decorators _remain_ in stage 2, and have not been rejected, signals that they still expect this to be -the case. This is also the feedback that the core team received in the January -TC39 meeting. _Crucially_, all parties were in agreement about the _invocation_ -syntax of decorators: - -```js -class Person { - @decorate name; -} -``` - -This is the most stable part of the spec, and the one that average developers -will come into contact with most. It means that changes to the spec will likely -affect the _application_ of decorators, which will in turn affect library and -framework maintainers, but **not** end users in most cases. This places the -burden of dealing with the spec changes on the Ember.js core team, and addon -authors who wish to adopt decorators while they are in stage 2. - -As such, adopting framework-provided decorators now should present a minimal amount of risk to -Ember and its users, and while it is not ideal, it allows us to continue moving -forward and innovating as a framework. +the case. However, it is clear based on the initial work following the January +TC39 meeting that [the proposal could change +significantly](https://github.com/tc39/proposal-decorators/pull/250) between now +and stage 3. + +Parts of the proposal, such as how decorators are invoked, seem solid based on +the feedback we received at the January TC39 meeting, and based on the draft of +the new spec. The definition of decorators is the most likely thing to change. +As such, user code should be minimally affected by any changes, and most changes +should be codemod-able. This reduces the risk of adopting decorators now, since +code _written_ with decorators shouldn't need to change that much. + +[The current recommendation from the authors of the +spec](https://github.com/tc39/proposal-decorators/tree/static#how-should-i-use-decorators-in-transpilers-today) +is to use the stage 1 decorators proposal until the next iteration is ready. +Stage 1 decorators are very stable, and used by a large community of developers +outside of Ember. By standardizing on this version of the spec, we'll be able to +gain the benefits of using a shared solution with the wider ecosystem while we +wait for the next iteration. ## Detailed design @@ -72,82 +71,150 @@ Notably, `GlimmerComponent` will _not_ support classic class syntax, due to its constraint of having to support both Ember.js _and_ Glimmer.js, where the classic class model is not available. However, creating an API compatible classic class version using Ember's component manager API should be possible if -users want to write Glimmer-like component's with classic syntax. - -### Decorator Versioning and Support - -The list of constraints which we are optimizing for is as follows, from the -highest priority to the lowest priority: - -- Aligning with the decorator spec as it evolves over time. -- Ensuring the maintenance cost for end users of _Ember's framework-provided_ - decorators is as small as reasonably possible, accounting for changes in the - spec. -- Ensuring addons that make use of supported versions of decorators can be - consumed in all apps that have decorator support, even if decorator versions - mismatch. -- Minimizing the maintenance cost for end users of _addon provided decorators_ - as the spec evolves. -- Minimizing the increase to static asset sizes, resulting from adding decorator - support. - -Non-goals/non-constraints: - -- Interop with older versions of the spec, including Typescript's current - transforms. -- Supporting all future versions of the spec simultaneously throughout the - ecosystem. - -Because the decorators spec has changed while remaining in a stage 2, it's no -longer enough to refer to "stage 2" as a version. Since Babel will be providing -the transforms, we will use their versioning system. [This is currently in -development](https://github.com/babel/babel/pull/9360), but looks like it will -be based on the TC39 meeting that the spec was presented at. For instance, the -current transforms available in Babel are referred to as `nov-2018`. - -Ember will begin by supporting the _latest_ version of the decorators transform -provided by Babel. Currently this is the `nov-2018` version, but by the time -decorators are released this may change. - -The decorators version will be configurable on a _per-app/engine/addon_ basis. -This will allow the ecosystem to adopt decorators without fears that if an -application updates, it could break their decorators. If no version is -specified, the transform will default to the latest version supported by Ember -at that time. In many cases the only decorators in use will be Ember's, so this -allows the ecosystem to upgrade without additional maintenance costs. The exact -method for specifying the version will be determined during implementation, but -will likely be a configuration option or addon. - -When new transforms are released, Ember will continue to support older versions -of the transforms alongside the new ones until _at least_ the next LTS release, -with deprecation warnings for any users of the older transforms. This will mean -shipping multiple different versions of decorators at once, and providing a way -for apps/addons to import the correct version. Additionally, Ember will attempt -to provide addon authors with tools that allow _them_ to do this as well, so -they can support multiple versions of transforms easily and transparently. The -exact API for these tools is out of scope for this RFC, and will likely be -dependent on their implementation. +users want to write Glimmer-like components with classic syntax. -## How we teach this +### Decorator Semantics + +Ember will support Babel's stage 1 decorator transforms. Since Babel and +TypeScript's decorators overlap so much, most TypeScript decorators should also +work in Ember apps. However, due to subtle differences in the way Babel and +TypeScript's decorators work, we can't support them both in _all_ cases. In +cases where there are nuanced differences, Babel's transforms will be considered +the canonical source of truth. For `ember-cli-typescript` users this shouldn't +be an issue, since they can use Babel's transforms in the latest versions of +`ember-cli-typescript`. + +#### Class Field Assigment Order + +Ember also does _not_ guarantee a particular ordering for the assignment of +decorated fields in order to support users who want to use _native_ class fields +when they are available. All that Ember guarantees is that by the time the +`constructor`'s `super` method completes (or the constructor code begins, if the +class is not extending), all class fields will be assigned. + +Assigning class fields based on the values of other class fields is somewhat of +an anti-pattern as is, so this would reinforce discouraging problematic +patterns. To be clear: + +```js +class MyComponent extends Component { + // This is ok, because the component instance exists + // for all class fields. + stateManager = new StateManager(this); + + @service time; + + // This is ok, because the `time` is an injection, + // and always available for all class fields. + createdAt = this.time.now(); + + // This is problematic in general, not just with decorators, because + // it relies on the order of class field assignment within this class. + // It is a refactoring hazard, and should likely be done in the + // constructor instead. + registry = new Registry(); + container = new Container(this.registry); +} +``` + +Ember will attempt to provide a lint rule which can handle this level of nuance +and prevent frustration when users copy and paste code around the class body. -As mentioned in the motivation section, the _invocation_ side of decorators -seems to be much more stable currently, so we ideally will not have to teach end -consumers too much about the details of this system. We should definitely -provide a disclaimer that outlines how our support structure works, and how -users can lock their version if they are using custom decorators. This should be -included _early_ in the guides. +#### Class Field Assignment Semantics + +Currently, the Babel stage 1 transforms require `loose` mode for class fields +which causes them to be assigned directly rather than using +`Object.defineProperty`, which is not inline with the class fields spec. The +biggest difference in behavior is when a child class attempts to override a +getter/setter on the parent class: + +```js +class Foo { + get baz() { + return this._baz; + } + + set baz(value) { + this._baz = value; + } +} + +class Bar extends Foo { + baz = 123; +} +``` + +In strict mode, `baz` in the child class would completely override the +getter/setter, and they would not exist/be usable on an instance of `Bar` +(except through calls to `super`). In loose mode, `baz` in the child class would +go through the getter/setter, and be assigned to `_baz` on the instance. + +In order to mitigate this, Ember will provide an assertion in development builds +via a babel transform for fields that are assigned in a non-spec compliant way +that throws in this scenario. This will prevent users from accidentally writing +code that will be hard to migrate forward to class fields in the future. + +### Decorator Support Timeframe + +Stage 1 decorators will be considered a first class Ember API. They will be +supported until: + +1. Decorators have reached at least Stage 3 in TC39, with strong confidence that + they will not change significantly from Stage 3 -> 4. +2. A new RFC has been made to introduce Stage 3 decorators as a parallel API to + the stage 1 decoraters. +3. A deprecation RFC has been created for Stage 1 decorators. + +They will follow the same deprecation flow, and same SemVer requirements, as any +other Ember feature. + +## How we teach this -We should also provide thorough documentation for any tooling provided to help -addon authors write their own decorators. This will be a bit more of a moving -target, since this RFC does not define exact APIs and they will not be a -priority until Ember itself prepares to support multiple versions of the -transforms. +There are a few different important aspects of this that should be taught: + +1. **Class Field and Decorator semantics**, specifically around the ordering of + class fields. While any step to change a user's class field ordering would + likely be based on their target browsers and configuration, and would be + unlikely to change in a patch or minor version release, users writing code + that could be difficult to upgrade or dependent on a particular version of + the class field/decorator transforms is a failure case we want to avoid. +2. **Support timeframe and alternatives.** Users should be aware that this will + be a feature that will _likely_ change in the future, if only subtly. We + should highlight that there _is_ an alternative, classic class syntax, and + that it will still be fully supported. Ultimately, this point is about + setting expectations - that a year or so from now, when decorators are truly + final, there will be another shift. +3. **Custom Decorators.** We should document how users can write their own + decorators, but also caution against behaviors that will be difficult to + replicate in Stage 3 decorators when they are ready. This will _necessarily_ + be a moving target, since stage 3 decorators are not yet finished, but we can + try our best to recommend against usages that could be problematic. + + We should also warn that decorator definition code will almost definitely + have to be rewritten for stage 3. This should be very clear, so that users + understand that adopting custom decorators in their apps means taking on more + tech debt than just using official Ember decorators, and decorators provided + by addons. ## Drawbacks -The fact that decorators did not move to stage 3 signals that there may be -additional changes to the spec in the near future. Adopting them now could cause -more churn than it's worth. +- The fact that decorators did not move to stage 3 signals that there may be + additional changes to the spec in the near future. Adopting them now could + cause more churn than it's worth. + +- By adopting decorators now, we are essentially taking on some amount of debt + that we know will have to be repaid in the future as a community. This is less + than ideal, since we know that standardization is coming, it's just a matter + of when. This is also the situation we've been in for quite some time already, + and with the latest turn of events, it seems unlikely that decorators will be + fully standardized within the year. + + We can continue to wait, but there is no deadline on the design process, and + we could be stuck here indefinitely. This move is pragmatic in that it + unblocks us for now, and moves us toward using modern syntax that will be + compatible with stage 3 decorators - it allows us to begin unwinding other + layers of technical debt. It also represents minimal risk and debt to _users_ + of decorators, since the invocation style will likely be the same. ## Alternatives From 3cd45c8b6f037bc29dcd104f8b996c27db66d568 Mon Sep 17 00:00:00 2001 From: Chris Garrett Date: Fri, 8 Mar 2019 12:38:33 -0800 Subject: [PATCH 7/8] loosen stage 3 constraint --- text/0000-decorator-support.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/text/0000-decorator-support.md b/text/0000-decorator-support.md index aba1b67fa6..c0ef64086a 100644 --- a/text/0000-decorator-support.md +++ b/text/0000-decorator-support.md @@ -8,7 +8,7 @@ ## Summary Move forward with the Decorators RFC via stage 1 decorators, and await a stable -stage 3+ proposal. +future decorators proposal. ## Motivation @@ -159,11 +159,9 @@ code that will be hard to migrate forward to class fields in the future. Stage 1 decorators will be considered a first class Ember API. They will be supported until: -1. Decorators have reached at least Stage 3 in TC39, with strong confidence that - they will not change significantly from Stage 3 -> 4. -2. A new RFC has been made to introduce Stage 3 decorators as a parallel API to +1. A new RFC has been made to introduce a new decorator API as a parallel API to the stage 1 decoraters. -3. A deprecation RFC has been created for Stage 1 decorators. +2. A deprecation RFC has been created for Stage 1 decorators. They will follow the same deprecation flow, and same SemVer requirements, as any other Ember feature. From 3ae9a82459b37eed580494fc4d273d0b9f99aef3 Mon Sep 17 00:00:00 2001 From: Chris Garrett Date: Fri, 15 Mar 2019 16:12:27 -0700 Subject: [PATCH 8/8] Rename 0000-decorator-support.md to 0440-decorator-support.md --- text/{0000-decorator-support.md => 0440-decorator-support.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename text/{0000-decorator-support.md => 0440-decorator-support.md} (100%) diff --git a/text/0000-decorator-support.md b/text/0440-decorator-support.md similarity index 100% rename from text/0000-decorator-support.md rename to text/0440-decorator-support.md