Skip to content

Commit

Permalink
feat: automatically send assets with content/fallback requests as 103…
Browse files Browse the repository at this point in the history
… early hints
  • Loading branch information
digitalsadhu committed Aug 19, 2024
1 parent 312679f commit c4c8c88
Showing 1 changed file with 55 additions and 12 deletions.
67 changes: 55 additions & 12 deletions lib/podlet.js
Original file line number Diff line number Diff line change
Expand Up @@ -515,9 +515,9 @@ export default class PodiumPodlet {
* Takes an AssetCss instance or an object with equivalent properties, converts it to an AssetCss instance if necessary and adds it to the
* cssRoute array.
* @param { AssetCss | AssetCssLike } options
* @returns {void}
* @returns {AssetCss}
*/
#addCssAsset(options) {
#cssAsset(options) {
const clonedOptions = JSON.parse(JSON.stringify(options));
clonedOptions.value = this.#sanitize(
clonedOptions.value,
Expand All @@ -528,7 +528,7 @@ export default class PodiumPodlet {
...clonedOptions,
pathname: this.#pathname,
};
this.cssRoute.push(new AssetCss(args));
return new AssetCss(args);
}

/**
Expand All @@ -553,20 +553,19 @@ export default class PodiumPodlet {
css(options) {
if (Array.isArray(options)) {
for (const opts of options) {
this.#addCssAsset(opts);
this.cssRoute.push(this.#cssAsset(opts));
}
return;
}
this.#addCssAsset(options);
this.cssRoute.push(this.#cssAsset(options));
}

/**
* Takes an AssetJs instance or an object with equivalent properties, converts it to an AssetJs instance if necessary and adds it to the
* jsRoute array.
* Takes an AssetJs instance or an object with equivalent properties, converts it to an AssetJs instance if necessary and returns it.
* @param { AssetJs | AssetJsLike } options
* @returns {void}
* @returns {AssetJs}
*/
#addJsAsset(options) {
#jsAsset(options) {
const clonedOptions = JSON.parse(JSON.stringify(options));
clonedOptions.value = this.#sanitize(
clonedOptions.value,
Expand All @@ -591,7 +590,7 @@ export default class PodiumPodlet {
args.data = data;
}

this.jsRoute.push(new AssetJs(args));
return new AssetJs(args);
}

/**
Expand All @@ -616,11 +615,11 @@ export default class PodiumPodlet {
js(options) {
if (Array.isArray(options)) {
for (const opts of options) {
this.#addJsAsset(opts);
this.jsRoute.push(this.#jsAsset(opts));
}
return;
}
this.#addJsAsset(options);
this.jsRoute.push(this.#jsAsset(options));
}

/**
Expand Down Expand Up @@ -908,6 +907,50 @@ export default class PodiumPodlet {
res.header('podlet-version', this.version);
}

/**
use early hints to send assets to the client before the rest of the podlet response is sent.
using a new method like this and bypassing existing mechanisms is 100% backwards compatible from the podlet point of view.
eg.
app.get('/', (req, res) => {
res.assets([
{ value: 'https://example.com/style.css' }
{ value: 'https://example.com/script.js' }
]);
// do some complicated slow async stuff
res.podiumSend(`<div>rest of complicated slow stuff here</div>`)
});
*/
res.assets = (options) => {
// normalise array/object to array
let assets = options;
if (!Array.isArray(options)) {
assets = [options];
}

// detect css and js and map to correct asset objects
assets = assets
.map((asset) => {
if (asset.value.endsWith('.css')) {
return this.#cssAsset(asset);
} else if (asset.value.endsWith('.js')) {
return this.#jsAsset(asset);
}
})
// add a toLinkHeader method to the asset objects
// that converts all the properties to a link header
// eg. <https://example.com/style.css>; rel="stylesheet; as=style; type=text/css; etc"
.map((asset) => asset.toLinkHeader());

// send Link header as a 103 early hint to the layout client which can then forward on the link header
// to the browser
res.setHeader('Link', assets.join(', '));
res.status(103);
res.send();
};

res.podiumSend = (data, ...args) =>
res.send(this.render(incoming, data, ...args));

Expand Down

0 comments on commit c4c8c88

Please sign in to comment.