From 0b884c1d0d11f8f4d942073bdcfa1bce900d985d Mon Sep 17 00:00:00 2001 From: Jonathan Jackson Date: Fri, 23 Feb 2018 14:54:41 -0500 Subject: [PATCH 1/8] Allow enviroment set _renderMode to be used in Visit This is part of an arching plan to introduce Glimmer's rehydration/serializtion modes to Ember proper. There are 4 PR's that are all interwoven, of which, this is one. - [ ] Glimmer.js: glimmerjs/glimmer-vm#783 (comment) - [ ] Ember.js: emberjs/ember.js#16227 - [ ] Fastboot: https://github.com/ember-fastboot/fastboot/pull/185 - [ ] EmberCLI Fastboot: ember-fastboot/ember-cli-fastboot#580 In order of need to land they are: Glimmer.js: glimmerjs/glimmer-vm#783 (comment) This resolves a rather intimate API problem. Glimmer-vm expects a very specific comment node to exist to know whether or not the rehydration element builder can do it's job properly. If it is not found on the first node from `rootElement` it throws. In fastboot however we are certain that there will already be existant elements in the way that will happen before rendered content. This PR just iterates through the nodes until it finds the expected comment node. And only throws if it never finds one. --- Ember.js: emberjs/ember.js#16227 This PR modifies the `visit` API to allow a private _renderMode to be set to either serialize or rehydrate. In SSR environments the assumption (as it is here in this fastboot PR) is that we'll set the visit API to serialize mode which ensures glimmer-vm's serialize element builder is used to build the API. The serialize element builder ensures that we have the necessary fidelty to rehydrate correctly and is mandatory input for rehydration. --- Fastboot: https://github.com/ember-fastboot/fastboot/pull/185 This allows enviroment variable to set _renderMode to be used in Visit API. Fastboot must send content to browser made with the serialization element builder to ensure rehydration can be sucessful. --- EmberCLI Fastboot: ember-fastboot/ember-cli-fastboot#580 Finally this does the fun part of disabling the current clear-double-render instance-initializer We first check to ensure we are in a non-fastboot environment. Then we ensure that we can find the expected comment node from glimmer-vm's serialize element builder. This ensures that this change will only effect peoeple who use ember-cli-fastboot with the serialized output from the currently experimental fastboot setup Then we ensure `ApplicationInstance#_bootSync` specifies the rehydrate _renderMode. This is done in `_bootSync` this way because visit is currently not used to boot ember applications. And we must instead set bootOptions this way instead. We also remove the markings for `fastboot-body-start` and `fastboot-body-end` to ensure clear-double render instance-initializer is never called. --- index.js | 1 + package.json | 2 +- vendor/experimental-render-mode-rehydrate.js | 25 ++++++++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 vendor/experimental-render-mode-rehydrate.js diff --git a/index.js b/index.js index aab2e30cf..2234d48f1 100644 --- a/index.js +++ b/index.js @@ -58,6 +58,7 @@ module.exports = { app.options.fingerprint.generateAssetMap = true; } + app.import('vendor/experimental-render-mode-rehydrate.js'); // get the app registry object and app name so that we can build the fastboot // tree this._appRegistry = app.registry; diff --git a/package.json b/package.json index 4b7849e42..4c588a511 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "ember-cli-lodash-subset": "2.0.1", "ember-cli-preprocess-registry": "^3.1.0", "ember-cli-version-checker": "^2.1.0", - "fastboot": "^1.1.3", + "fastboot": "github:rondale-sc/fastboot#utilize-rehydration-serialization-from-glimmer", "fastboot-express-middleware": "^1.1.0", "fastboot-transform": "^0.1.2", "fs-extra": "^4.0.2", diff --git a/vendor/experimental-render-mode-rehydrate.js b/vendor/experimental-render-mode-rehydrate.js new file mode 100644 index 000000000..91439adc8 --- /dev/null +++ b/vendor/experimental-render-mode-rehydrate.js @@ -0,0 +1,25 @@ +(function() { + if (typeof FastBoot === 'undefined') { + var current = document.getElementById('fastboot-body-start'); + + if (current && current.nextSibling.nodeValue === '%+b:0%') { + Ember.ApplicationInstance.reopen({ + _bootSync: function(options) { + if (options === undefined) { + options = { + _renderMode: 'rehydrate' + }; + } + + return this._super(options); + } + }); + + // Prevent clearRender by removing `fastboot-body-start` which is already + // guarded for + current.parentNode.removeChild(current); + var end = document.getElementById('fastboot-body-end'); + end.parentNode.removeChild(end); + } + } +})(); From b3d258976886cced131c36440fdefa951436a4ef Mon Sep 17 00:00:00 2001 From: Jonathan Jackson Date: Tue, 27 Feb 2018 13:41:25 -0800 Subject: [PATCH 2/8] Utilize Ember.ViewUtils.isSerializationFirstNode to determine action This is dependent on https://github.com/glimmerjs/glimmer-vm/pull/788 The idea is to use a function exported by ember-glimmer rather than rely on magic glimmer-vm string. --- vendor/experimental-render-mode-rehydrate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/experimental-render-mode-rehydrate.js b/vendor/experimental-render-mode-rehydrate.js index 91439adc8..c20490fd3 100644 --- a/vendor/experimental-render-mode-rehydrate.js +++ b/vendor/experimental-render-mode-rehydrate.js @@ -2,7 +2,7 @@ if (typeof FastBoot === 'undefined') { var current = document.getElementById('fastboot-body-start'); - if (current && current.nextSibling.nodeValue === '%+b:0%') { + if (current && Ember.ViewUtils.isSerializationFirstNode(current.nextSibling)) { Ember.ApplicationInstance.reopen({ _bootSync: function(options) { if (options === undefined) { From 725aedb3d92f5090a4f65c67589c5d1a16ec1ddd Mon Sep 17 00:00:00 2001 From: Jonathan Jackson Date: Sat, 10 Mar 2018 14:26:34 -0800 Subject: [PATCH 3/8] Add readme explaining how to opt-in to rehydration --- README.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/README.md b/README.md index a64fb5867..b5953e3f5 100644 --- a/README.md +++ b/README.md @@ -399,6 +399,41 @@ export default Ember.Route.extend({ ``` And they still take advantage of caching in the `shoebox`. No more redundant AJAX for already acquired data. Installation details are available in the addon [README](https://github.com/appchance/ember-cached-shoe#ember-cached-shoe). +### Rehydration + +What is Rehydration? + +The rehydration feature means that the Glimmer VM can take a DOM tree +created using Server Side Rendering (SSR) and use it as the starting +point for the append pass. + +See details here: + +https://github.com/glimmerjs/glimmer-vm/commit/316805b9175e01698120b9566ec51c88d075026a + +In order to utilize rehydration in Ember.js applications we need to ensure that +both server side renderers (like fastboot) properly encode the DOM they send to +the browser with the serialization format (introduced in the commit above) AND +that the browser instantiated Ember.js application knows to use the rehydration +builder to consume that DOM. + +Rehydration is 100% opt-in, if you do not specify the environment flag your +application will behave as it did before! + +We can opt-in to the rehydration filter by setting the following environment +flag: + +``` +EXPERIMENTAL_RENDER_MODE_SERIALIZE=true +``` + +This flag is read by Ember CLI Fastboot's dependency; fastboot to alert it to +produce DOM with the glimmer-vm's serialization element builder. This addon +(Ember CLI Fastboot) then uses a utility function from glimmer-vm that allows +it to know whether or not the DOM it received in the browser side was generated +by the serialization builder. If it was, it tells the Ember.js Application to +use the rehydration builder and your application will be using rehydration. + ## Build Hooks for FastBoot ### Disabling incompatible dependencies From 91c4f970638f6e88538342695d8b8fbedef75f1a Mon Sep 17 00:00:00 2001 From: Jonathan Jackson Date: Sat, 10 Mar 2018 16:15:14 -0800 Subject: [PATCH 4/8] Ensure isSerializationFirstNode is defined --- vendor/experimental-render-mode-rehydrate.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/vendor/experimental-render-mode-rehydrate.js b/vendor/experimental-render-mode-rehydrate.js index c20490fd3..0ade42e5c 100644 --- a/vendor/experimental-render-mode-rehydrate.js +++ b/vendor/experimental-render-mode-rehydrate.js @@ -2,7 +2,11 @@ if (typeof FastBoot === 'undefined') { var current = document.getElementById('fastboot-body-start'); - if (current && Ember.ViewUtils.isSerializationFirstNode(current.nextSibling)) { + if ( + current && + typeof Ember.ViewUtils.isSerializationFirstNode === 'function' && + Ember.ViewUtils.isSerializationFirstNode(current.nextSibling) + ) { Ember.ApplicationInstance.reopen({ _bootSync: function(options) { if (options === undefined) { From 6bbffd36b417e5a4ec1319adaa752a25ad3cd500 Mon Sep 17 00:00:00 2001 From: Jonathan Jackson Date: Sun, 11 Mar 2018 11:36:51 -0700 Subject: [PATCH 5/8] Add version limitations to section in README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index b5953e3f5..6524af875 100644 --- a/README.md +++ b/README.md @@ -434,6 +434,8 @@ it to know whether or not the DOM it received in the browser side was generated by the serialization builder. If it was, it tells the Ember.js Application to use the rehydration builder and your application will be using rehydration. +Rehydration is only compatible with fastboot > 1.1.4, and Ember.js > 3.2. + ## Build Hooks for FastBoot ### Disabling incompatible dependencies From d896108efd4dc8222f04a39713a8785e4af18125 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Tue, 13 Mar 2018 10:45:13 -0700 Subject: [PATCH 6/8] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6524af875..0ace6a411 100644 --- a/README.md +++ b/README.md @@ -434,7 +434,7 @@ it to know whether or not the DOM it received in the browser side was generated by the serialization builder. If it was, it tells the Ember.js Application to use the rehydration builder and your application will be using rehydration. -Rehydration is only compatible with fastboot > 1.1.4, and Ember.js > 3.2. +Rehydration is only compatible with fastboot > 1.1.4-beta.1, and Ember.js > 3.2. ## Build Hooks for FastBoot From 0fceedcdf5bed9dbfb976526f8f1162cafa79fc9 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Tue, 13 Mar 2018 10:47:39 -0700 Subject: [PATCH 7/8] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4c588a511..fe0243714 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "ember-cli-lodash-subset": "2.0.1", "ember-cli-preprocess-registry": "^3.1.0", "ember-cli-version-checker": "^2.1.0", - "fastboot": "github:rondale-sc/fastboot#utilize-rehydration-serialization-from-glimmer", + "fastboot": "^1.1.4-beta.1", "fastboot-express-middleware": "^1.1.0", "fastboot-transform": "^0.1.2", "fs-extra": "^4.0.2", From dc53d0d342f21adc5216f5e1a8b836953c5635db Mon Sep 17 00:00:00 2001 From: Cory Date: Tue, 13 Mar 2018 10:57:31 -0700 Subject: [PATCH 8/8] Update yarn.lock --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 79aa8fdfe..f9ed6632a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2865,9 +2865,9 @@ fastboot@^1.1.2: simple-dom "^1.0.0" source-map-support "^0.5.0" -fastboot@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/fastboot/-/fastboot-1.1.3.tgz#56c5f56415c5ae8de2db539c0d3ecbcd65538f8b" +fastboot@^1.1.4-beta.1: + version "1.1.4-beta.1" + resolved "https://registry.yarnpkg.com/fastboot/-/fastboot-1.1.4-beta.1.tgz#860f8af2bd032b3a7477fdf6e1a64b034a02829f" dependencies: chalk "^2.0.1" cookie "^0.3.1"