From 78b7d09fc381fd1d304fd45fe37e15f575b11552 Mon Sep 17 00:00:00 2001 From: Mehul Kar Date: Thu, 9 Jan 2020 17:39:41 -0800 Subject: [PATCH 1/7] App Boot Hooks --- text/0000-app-boot-hooks.md | 100 ++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 text/0000-app-boot-hooks.md diff --git a/text/0000-app-boot-hooks.md b/text/0000-app-boot-hooks.md new file mode 100644 index 0000000000..558f7067a3 --- /dev/null +++ b/text/0000-app-boot-hooks.md @@ -0,0 +1,100 @@ +- Start Date: 2020-01-09 +- Relevant Team(s): Ember.js +- RFC PR: (after opening the RFC PR, update this with a link to it and update the file name) +- Tracking: + +# App Boot Hooks + +## Summary + +This RFC proposes adding two hooks in the `Application` class in `app.js` that, by default, load +and run initializers and instance-initializers. Here's an eaxmple of what this could look like: + +```js +import { loadInitializers, loadInstanceInitializers } from 'ember-load-initializers'; +import Application from '@ember/application'; +import config from './config/environment'; + +export default class App extends Application { + onBoot() { + loadInitializers(this, config.modulePrefix); + this.runInitializers(); + } + onInstanceBoot(appInstance) { + loadInstanceInitializers(this, config.modulePrefix) + this.runInstanceInitializers(); + } +} +``` + +This implementation would be backwards compatible and would give users complete control of +*how* and *when* they want to run their initializers and it would allow the beginner experience to +be progressively enhanced. + +## Motivation + +- The existing convention of initializer code comes from Ruby on Rails (which has similar +`config/initializers/*.rb` files). This technique works and can be nice for organizing code, but it +is a poor onboarding experience and misses out on a teaching opportunity about how Applications work. +By making it possible to write code as simple as `console.log()` in `app.js`, Ember developers can +be exposed to the entry point of the application much sooner and avoid the complication of additional +directories. +- Currently the order of initializers is not guaranteed without some extra annotations +(`before`/ `after`) that are both error-prone and not obvious. It can be debated whether +two separate initializers *should* care about whether the other one has run or not, it is reasonable +to expect that two unrelated pieces of code will run in the same order every time. This may already +be technically true due to the implemented sorting algorithm, it is not guaranteed by Ember, and +can be messy to grapple with. By using the hooks proposed in the Summary section, Ember developers +can simply write Javascript and expect that it runs the way they intend it to. + +## Detailed design + +- Add the `onBoot` and `onBootInstance` methods and move the code that runs initializers into these +hooks. +- Expose the hooks and their default implementations into the app blueprint so users can change them +as desired. +- Export a `loadInstanceInitializers` function from the `ember-load-initializers` addon and separate +move the code from `loadInitializers` related to instance initializers into it. + +## How we teach this + +The Ember Guides currently reference initializers mainly on one page: +[https://guides.emberjs.com/release/applications/initializers/][1]. +This page would need to be updated to cover the methods proposed above. + +## Drawbacks + +Good reasons for separate files per initializer are: + +- keeping "concerns separated" + - this is a valid point for large applications, as it can be useful to encourage team members + to write new functions rather than writing long `onBoot` functions. It's also reasonable to say + that choosing the order of initializers should not be a developer concern. However, it is both + easy to write a messy initializer function in one file, and possible to have tooling that + prevents a long `onBoot` function. I believe the benefits of `onBoot` outweigh these concerns. +- treeshaking + - This is also a valid concern, but in a large enough application, it is possible to continue + using the existing implementation. It's also possible to design the initializer running code + such that only ONE of the methodologies is possible (functions exported from a directory + vs `onBoot`). +- container registration + - Files in the appropriate app directories are automatically registered into the application + container. Removing code from this directory location and allowing it to sit inline in app.js + means that initialization functions can no longer be looked up. I have never heard of this + functionality being used intentionally in production, so I don't think it's a problem. + +## Alternatives + +- It is already possible to override the `boot` method in `Application` to get this feature, +but it would require re-implementing a large part of the interaction between Application and +ApplicationInstance, it is easily possible for an application to dig itself into a maintenance +nightmare. + +## Unresolved questions + +- What constraints, if any, do we want to apply to this free-for-all method? +- Would it be better if the `onBoot` method implemented `loadInitializers` upstream, and then +the user `App` is required to call `super` instead? + + +[1]: https://guides.emberjs.com/release/applications/initializers/ From a658b904687ae35575213cec4f577bc53cbc63ad Mon Sep 17 00:00:00 2001 From: Mehul Kar Date: Thu, 9 Jan 2020 17:40:50 -0800 Subject: [PATCH 2/7] Add PR link --- text/0000-app-boot-hooks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-app-boot-hooks.md b/text/0000-app-boot-hooks.md index 558f7067a3..842a5744b2 100644 --- a/text/0000-app-boot-hooks.md +++ b/text/0000-app-boot-hooks.md @@ -1,6 +1,6 @@ - Start Date: 2020-01-09 - Relevant Team(s): Ember.js -- RFC PR: (after opening the RFC PR, update this with a link to it and update the file name) +- RFC PR: https://github.com/emberjs/rfcs/pull/573 - Tracking: # App Boot Hooks From 69a3fe26e0cd934bc8f48d79301244681b67a98b Mon Sep 17 00:00:00 2001 From: Mehul Kar Date: Thu, 6 Aug 2020 16:16:23 -0700 Subject: [PATCH 3/7] add rfc 572 as alternative --- text/{0000-app-boot-hooks.md => 0573-app-boot-hooks.md} | 5 +++++ 1 file changed, 5 insertions(+) rename text/{0000-app-boot-hooks.md => 0573-app-boot-hooks.md} (90%) diff --git a/text/0000-app-boot-hooks.md b/text/0573-app-boot-hooks.md similarity index 90% rename from text/0000-app-boot-hooks.md rename to text/0573-app-boot-hooks.md index 842a5744b2..ddbc2f0f82 100644 --- a/text/0000-app-boot-hooks.md +++ b/text/0573-app-boot-hooks.md @@ -89,6 +89,11 @@ Good reasons for separate files per initializer are: but it would require re-implementing a large part of the interaction between Application and ApplicationInstance, it is easily possible for an application to dig itself into a maintenance nightmare. +- Make initializers more robust and allow them to return promises. This was detailed in [RFC #572], +but the cost of initializers (both in bundle size and maintenance) is already high and it would be +better to drop them entirely. One benefit of initializers that would we would lose is the ability +for addons to inject initialization behavior into their host apps implicitly. While this is useful, +it is also harder to debug this pattern and ultimately makes it harder to maintain Ember apps. ## Unresolved questions From 0700476d8e838ade7189e90c98448247f0b9b46b Mon Sep 17 00:00:00 2001 From: Mehul Kar Date: Thu, 6 Aug 2020 16:25:45 -0700 Subject: [PATCH 4/7] add two more motivations --- text/0573-app-boot-hooks.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/text/0573-app-boot-hooks.md b/text/0573-app-boot-hooks.md index ddbc2f0f82..5f20acaefc 100644 --- a/text/0573-app-boot-hooks.md +++ b/text/0573-app-boot-hooks.md @@ -46,6 +46,15 @@ to expect that two unrelated pieces of code will run in the same order every tim be technically true due to the implemented sorting algorithm, it is not guaranteed by Ember, and can be messy to grapple with. By using the hooks proposed in the Summary section, Ember developers can simply write Javascript and expect that it runs the way they intend it to. +- **Blocking async code before routing**. Currently, the earliest place an application can write +asynchronous blocking code is in the `beforeModel` hook of the `ApplicationRoute`. +In some cases, this is too late. For example, if an application needs to wait for an event +from its host environment e.g. an iframe or an electron shell, and use it to determine +the starting URL, it cannot do so. +- **First Transition**: Similarly, if one wants to add an event listener to the +`routeWillChange` event, +it is too late to do that in the beforeModel hook, because the event has already +been dispatched for the first route transition. ## Detailed design From 54137c738d027e63bfc133314d98395e7de7692d Mon Sep 17 00:00:00 2001 From: Mehul Kar Date: Thu, 6 Aug 2020 16:30:27 -0700 Subject: [PATCH 5/7] improve motivations section --- text/0573-app-boot-hooks.md | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/text/0573-app-boot-hooks.md b/text/0573-app-boot-hooks.md index 5f20acaefc..48195a77e4 100644 --- a/text/0573-app-boot-hooks.md +++ b/text/0573-app-boot-hooks.md @@ -33,28 +33,27 @@ be progressively enhanced. ## Motivation -- The existing convention of initializer code comes from Ruby on Rails (which has similar -`config/initializers/*.rb` files). This technique works and can be nice for organizing code, but it -is a poor onboarding experience and misses out on a teaching opportunity about how Applications work. -By making it possible to write code as simple as `console.log()` in `app.js`, Ember developers can -be exposed to the entry point of the application much sooner and avoid the complication of additional -directories. -- Currently the order of initializers is not guaranteed without some extra annotations -(`before`/ `after`) that are both error-prone and not obvious. It can be debated whether -two separate initializers *should* care about whether the other one has run or not, it is reasonable -to expect that two unrelated pieces of code will run in the same order every time. This may already -be technically true due to the implemented sorting algorithm, it is not guaranteed by Ember, and -can be messy to grapple with. By using the hooks proposed in the Summary section, Ember developers -can simply write Javascript and expect that it runs the way they intend it to. +- **Moving away from initializers**. The existing convention of initializer code comes from +Ruby on Rails (which has similar `config/initializers/*.rb` files). This technique works +and can be nice for organizing code, but it also has several downsides: + + - bookkeeping code to determine the order of execution + - confusing `before`/`after` API to change order of execution + - implicit injections from addons by way of merging directories + - lack of support for blocking asynchronous behavior for instance-initializers (`{defer|advance}Readiness` exists for initializers) + - a poor onboarding experience and misses out on a teaching opportunity about how Ember Applications work. + + By providing a way to hook into the existing, async boot sequence more directly, + application developers can write declarative, explicit code about how they want to + customize their application. - **Blocking async code before routing**. Currently, the earliest place an application can write asynchronous blocking code is in the `beforeModel` hook of the `ApplicationRoute`. In some cases, this is too late. For example, if an application needs to wait for an event from its host environment e.g. an iframe or an electron shell, and use it to determine the starting URL, it cannot do so. - **First Transition**: Similarly, if one wants to add an event listener to the -`routeWillChange` event, -it is too late to do that in the beforeModel hook, because the event has already -been dispatched for the first route transition. +`routeWillChange` event, it is too late to do that in the beforeModel hook, because the +event has already been dispatched for the first route transition. ## Detailed design From 1b92ffb84245449b2077fcfea4c6883af68dca7b Mon Sep 17 00:00:00 2001 From: Mehul Kar Date: Thu, 6 Aug 2020 16:32:29 -0700 Subject: [PATCH 6/7] add todo --- text/0573-app-boot-hooks.md | 1 + 1 file changed, 1 insertion(+) diff --git a/text/0573-app-boot-hooks.md b/text/0573-app-boot-hooks.md index 48195a77e4..6ee54e3a2b 100644 --- a/text/0573-app-boot-hooks.md +++ b/text/0573-app-boot-hooks.md @@ -108,6 +108,7 @@ it is also harder to debug this pattern and ultimately makes it harder to mainta - What constraints, if any, do we want to apply to this free-for-all method? - Would it be better if the `onBoot` method implemented `loadInitializers` upstream, and then the user `App` is required to call `super` instead? +- Bundle size improvements? Should we include the deprecation path for initializers? [1]: https://guides.emberjs.com/release/applications/initializers/ From ae63b40c692ec01f29c4dacb64de19d1a6f623fd Mon Sep 17 00:00:00 2001 From: Mehul Kar Date: Fri, 16 Apr 2021 12:26:59 -0700 Subject: [PATCH 7/7] Update text/0573-app-boot-hooks.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Clemens M�ller --- text/0573-app-boot-hooks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0573-app-boot-hooks.md b/text/0573-app-boot-hooks.md index 6ee54e3a2b..3312f6cec5 100644 --- a/text/0573-app-boot-hooks.md +++ b/text/0573-app-boot-hooks.md @@ -8,7 +8,7 @@ ## Summary This RFC proposes adding two hooks in the `Application` class in `app.js` that, by default, load -and run initializers and instance-initializers. Here's an eaxmple of what this could look like: +and run initializers and instance-initializers. Here's an example of what this could look like: ```js import { loadInitializers, loadInstanceInitializers } from 'ember-load-initializers';