Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Octane-ify runtime code #909

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,36 @@ export function clearHtml() {
let endMarker = document.getElementById('fastboot-body-end');

if (current && endMarker) {
let shoeboxNodes = document.querySelectorAll('[type="fastboot/shoebox"]');
let shoeboxNodesArray = []; // Note that IE11 doesn't support more concise options like Array.from, so we have to do something like this
for(let i=0; i < shoeboxNodes.length; i++){
shoeboxNodesArray.push(shoeboxNodes[i]);
}
let shoeboxNodes = Array.from(
document.querySelectorAll('[type="fastboot/shoebox"]')
);

let parent = current.parentElement;
let nextNode;
do {
nextNode = current.nextSibling;
parent.removeChild(current);
current = nextNode;
} while (nextNode && nextNode !== endMarker && shoeboxNodesArray.indexOf(nextNode) < 0);
} while (
nextNode &&
nextNode !== endMarker &&
!shoeboxNodes.includes(nextNode)
);
endMarker.parentElement.removeChild(endMarker);
}
}

export default {
name: "clear-double-boot",
name: 'clear-double-boot',

initialize(instance) {
if (typeof FastBoot === 'undefined') {
var originalDidCreateRootView = instance.didCreateRootView;

instance.didCreateRootView = function() {
instance.didCreateRootView = function () {
clearHtml();
originalDidCreateRootView.apply(instance, arguments);
};
}
}
}
},
};
57 changes: 33 additions & 24 deletions packages/ember-cli-fastboot/addon/locations/none.js
Original file line number Diff line number Diff line change
@@ -1,53 +1,62 @@
import { computed, get } from '@ember/object';
import { bool, readOnly } from '@ember/object/computed';
import { inject as service } from '@ember/service';
import { getOwner } from '@ember/application'
import NoneLocation from '@ember/routing/none-location'
import { getOwner } from '@ember/application';
import NoneLocation from '@ember/routing/none-location';

const TEMPORARY_REDIRECT_CODE = 307;

export default NoneLocation.extend({
implementation: 'fastboot',
fastboot: service(),
export default class FastbootLocation extends NoneLocation {
implementation = 'fastboot';

_config: computed(function () {
return getOwner(this).resolveRegistration('config:environment');
}),
@service fastboot;

_fastbootHeadersEnabled: bool('_config.fastboot.fastbootHeaders'),
#config;
get _config() {
return (
this.#config ??
(this.#config = getOwner(this).resolveRegistration('config:environment'))
);
}

get _fastbootHeadersEnabled() {
return this._config.fastboot.fastbootHeaders;
}

_redirectCode: computed(function () {
return get(this, '_config.fastboot.redirectCode') || TEMPORARY_REDIRECT_CODE;
}),
get _redirectCode() {
return this._config.fastboot.redirectCode || TEMPORARY_REDIRECT_CODE;
}

_response: readOnly('fastboot.response'),
_request: readOnly('fastboot.request'),
get _response() {
return this.fastboot.response;
}
get _request() {
return this.fastboot.request;
}

setURL(path) {
if (get(this, 'fastboot.isFastBoot')) {
let response = get(this, '_response');
let currentPath = get(this, 'path');
if (this.fastboot.isFastBoot) {
let response = this._response;
let currentPath = this.path;
let isInitialPath = !currentPath || currentPath.length === 0;

if (!isInitialPath) {
path = this.formatURL(path);
let isTransitioning = currentPath !== path;

if (isTransitioning) {
let host = get(this, '_request.host');
let host = this._request.host;
let redirectURL = `//${host}${path}`;

response.statusCode = this.get('_redirectCode');
response.statusCode = this._redirectCode;
response.headers.set('location', redirectURL);
}
}

// for testing and debugging
if (get(this, '_fastbootHeadersEnabled')) {
if (this._fastbootHeadersEnabled) {
response.headers.set('x-fastboot-path', path);
}
}

this._super(...arguments);
super.setURL(...arguments);
}
});
}
148 changes: 89 additions & 59 deletions packages/ember-cli-fastboot/addon/services/fastboot.js
Original file line number Diff line number Diff line change
@@ -1,113 +1,143 @@
/* global FastBoot */
import { getOwner } from '@ember/application';
import { computed, get } from '@ember/object';
import { readOnly } from '@ember/object/computed';
import { assert } from '@ember/debug';
import EObject from '@ember/object';
import Service from '@ember/service';

const RequestObject = EObject.extend({
init() {
this._super(...arguments);

let request = this.request;
delete this.request;

class RequestObject {
constructor(request) {
this.method = request.method;
this.body = request.body;
this.cookies = request.cookies;
this.headers = request.headers;
this.queryParams = request.queryParams;
this.path = request.path;
this.protocol = request.protocol;
this._host = function() {
this._host = function () {
return request.host();
};
},
}

host: computed(function() {
return this._host();
})
});
get host() {
return this._host;
}
}

class Shoebox {
/**
*
* @param {FastBootService} fastboot
*/
constructor(fastboot) {
this.fastboot = fastboot;
}

const Shoebox = EObject.extend({
put(key, value) {
assert('shoebox.put is only invoked from the FastBoot rendered application', this.get('fastboot.isFastBoot'));
assert(
'shoebox.put is only invoked from the FastBoot rendered application',
this.fastboot.isFastBoot
);
assert('the provided key is a string', typeof key === 'string');

let fastbootInfo = this.get('fastboot._fastbootInfo');
if (!fastbootInfo.shoebox) { fastbootInfo.shoebox = {}; }
let fastbootInfo = this.fastboot._fastbootInfo;
if (!fastbootInfo.shoebox) {
fastbootInfo.shoebox = {};
}

fastbootInfo.shoebox[key] = value;
},
}

retrieve(key) {
if (this.get('fastboot.isFastBoot')) {
let shoebox = this.get('fastboot._fastbootInfo.shoebox');
if (!shoebox) { return; }
if (this.fastboot.isFastBoot) {
let shoebox = this.fastboot._fastbootInfo.shoebox;
if (!shoebox) {
return;
}

return shoebox[key];
}

let shoeboxItem = this.get(key);
if (shoeboxItem) { return shoeboxItem; }
let shoeboxItem = this[key];
if (shoeboxItem) {
return shoeboxItem;
}

let el = document.querySelector(`#shoebox-${key}`);
if (!el) { return; }
if (!el) {
return;
}
let valueString = el.textContent;
if (!valueString) { return; }
if (!valueString) {
return;
}

shoeboxItem = JSON.parse(valueString);
this.set(key, shoeboxItem);
this[key] = shoeboxItem;

return shoeboxItem;
}
});
}

class FastBootService extends Service {
isFastBoot = typeof FastBoot !== 'undefined';

const FastBootService = Service.extend({
isFastBoot: typeof FastBoot !== 'undefined',
/** @type {Shoebox} */
shoebox;

isFastboot: computed(function() {
// eslint-disable-next-line getter-return
get isFastboot() {
assert(
'The fastboot service does not have an `isFastboot` property. This is likely a typo. Please use `isFastBoot` instead.',
false
);
}),
}

init() {
this._super(...arguments);
constructor(owner) {
super(owner);
this.shoebox = new Shoebox(this);
}

let shoebox = Shoebox.create({ fastboot: this });
this.set('shoebox', shoebox);
},
get response() {
return this._fastbootInfo?.response;
}

response: readOnly('_fastbootInfo.response'),
metadata: readOnly('_fastbootInfo.metadata'),
get metadata() {
return this._fastbootInfo?.metadata;
}

request: computed(function() {
#request;
get request() {
if (!this.isFastBoot) return null;
return RequestObject.create({ request: get(this, '_fastbootInfo.request') });
}),

// this getter/setter pair is to avoid deprecation from [RFC - 680](https://github.com/emberjs/rfcs/pull/680)
_fastbootInfo: computed({
get() {
if (this.__fastbootInfo) {
return this.__fastbootInfo;
}
if (!this.#request) {
// eslint-disable-next-line ember/no-side-effects
this.#request = RequestObject.create(this._fastbootInfo?.request);
}

return this.#request;
}

return getOwner(this).lookup('info:-fastboot');
},
set(_key, value) {
this.__fastbootInfo = value;
return value;
#fastbootInfo;

get _fastbootInfo() {
if (!this.#fastbootInfo) {
// eslint-disable-next-line ember/no-side-effects
this.#fastbootInfo = getOwner(this).lookup('info:-fastboot');
}
}),

return this.#fastbootInfo;
}

/** @internal */ // Intended for use only in tests!
set _fastbootInfo(value) {
this.#fastbootInfo = value;
}

deferRendering(promise) {
assert('deferRendering requires a promise or thennable object', typeof promise.then === 'function');
assert(
'deferRendering requires a promise or thennable object',
typeof promise.then === 'function'
);
this._fastbootInfo.deferRendering(promise);
}
});
}

export default FastBootService;
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,4 @@ module('Unit | Service | fastboot | shoebox', function(hooks) {

assert.strictEqual(service.get('shoebox').retrieve('foo'), 'bar');
});
});
});
13 changes: 13 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9819,6 +9819,19 @@ fastboot-transform@^0.1.3:
broccoli-stew "^1.5.0"
convert-source-map "^1.5.1"

fastboot@^3.0.3:
version "3.3.2"
resolved "https://registry.yarnpkg.com/fastboot/-/fastboot-3.3.2.tgz#bf1ac7b01937846b63423a88138e13eb50406d2e"
integrity sha512-2NKTW32GvEsDyBrdw1trW1JsbS+9/7sAQuKwkht12mNitimRrSKVLP2AxsM/HSXQE+aiET4XCfKdyeIy0kQbKQ==
dependencies:
chalk "^4.1.2"
cookie "^0.4.1"
debug "^4.3.3"
jsdom "^19.0.0"
resolve "^1.22.0"
simple-dom "^1.4.0"
source-map-support "^0.5.21"

fastq@^1.6.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.8.0.tgz#550e1f9f59bbc65fe185cb6a9b4d95357107f481"
Expand Down