@platform/react.ssr
Version:
A lightweight SSR (server-side-rendering) system for react apps bundled with ParcelJS and hosted on S3.
92 lines (91 loc) • 2.99 kB
JavaScript
import { fs, http, util, cheerio } from '../common';
import { pathToRegexp } from 'path-to-regexp';
export class Route {
constructor(args) {
this.site = args.site;
this.def = args.route;
}
static format(args) {
const { input } = args;
if (typeof input !== 'object') {
return undefined;
}
const entry = util.asString(input.entry);
const paths = Array.isArray(input.path) ? input.path : [input.path];
const path = paths.filter(path => Boolean(path) && typeof path === 'string');
return { entry, path };
}
get paths() {
return this.def.path;
}
get version() {
return util.firstSemver(this.site.bundle) || '0.0.0';
}
get bundleUrl() {
const base = util.stripSlashes(this.site.baseUrl);
const path = util.stripSlashes(this.site.bundle);
return `${base}/${path}`;
}
async entry(args = {}) {
if (this._entry && !args.force) {
return this._entry;
}
const filename = this.def.entry;
const url = `${this.bundleUrl}/${filename}`;
const res = await http.get(url);
let status = 200;
if (!res.ok) {
status = res.status;
}
let html = res.ok ? res.text : '';
const version = this.version;
html = this.formatHtml({ html, filename, version });
const ok = status.toString().startsWith('2');
const entry = {
ok,
status,
url,
html,
};
this._entry = entry;
return entry;
}
isMatch(path) {
if (!this._regexps) {
this._regexps = this.paths.map(pattern => pathToRegexp(pattern));
}
return this._regexps.some(regex => Boolean(regex.exec(path)));
}
toObject() {
return Object.assign({}, this.def);
}
formatHtml(args) {
const { filename, html, version } = args;
if (!html) {
return html;
}
const site = this.site;
const files = site.files;
const entry = site.entries.find(item => item.file === filename);
const $ = cheerio.load(html);
$('html').attr('data-version', version);
$('html').attr('data-size', site.size);
if (entry) {
$(`div#${entry.id}`).html(entry.html);
$('head').append(`<style>${entry.css}</style>`);
}
files
.filter(file => file.path.endsWith('.js'))
.forEach(file => sizeAttr(file.bytes, $(`script[src="${fs.basename(file.path)}"]`)));
files
.filter(file => file.path.endsWith('.css'))
.forEach(file => sizeAttr(file.bytes, $(`link[href="${fs.basename(file.path)}"]`)));
return $.html();
}
}
Route.create = (args) => new Route(args);
function sizeAttr(bytes, el) {
if (el.length > 0) {
el.attr('data-size', fs.size.toString(bytes, { round: 0 }));
}
}