UNPKG

@platform/react.ssr

Version:

A lightweight SSR (server-side-rendering) system for react apps bundled with ParcelJS and hosted on S3.

135 lines (134 loc) 4.55 kB
import { constants, http, jsYaml, util } from '../common'; import { Route } from './Route'; export class Site { constructor(args) { const { index, manifest } = args; this.index = index; this.manifest = manifest; this._regexes = toDomainRegexes(this.def.domain); if (!this.name) { throw new Error(`A site definition must have a name.`); } } static async formatMany(args) { const { baseUrl, loadBundleManifest } = args; if (!Array.isArray(args.input)) { const error = `The manifest YAML "sites" field is not an array.`; throw new Error(error); } let sites = []; for (const input of args.input) { const site = await Site.formatOne({ input, baseUrl, loadBundleManifest }); sites = site ? [...sites, site] : sites; } return sites; } static async formatOne(args) { const { input, baseUrl } = args; if (typeof input !== 'object') { return; } const name = (input.name || '').trim(); let domain = input.domain || ''; domain = Array.isArray(domain) ? domain : [domain]; domain = domain.map((hostname) => util.stripHttp(hostname)); const bundle = util.asString(input.bundle).replace(/\/*$/, ''); let routes = typeof input.routes === 'object' ? input.routes : {}; routes = Object.keys(routes).reduce((acc, next) => { const input = routes[next]; if (input) { const route = Route.format({ input }); if (route) { acc[next] = route; } } return acc; }, {}); let files = []; let entries = []; let size = '-'; let bytes = -1; if (args.loadBundleManifest) { const bundleUrl = `${baseUrl}/${bundle}/${constants.PATH.BUNDLE_MANIFEST}`; const res = await http.get(bundleUrl); const bundleManifest = res.ok ? jsYaml.safeLoad(res.body) : undefined; if (bundleManifest) { size = bundleManifest.size; bytes = bundleManifest.bytes; files = bundleManifest.files || []; entries = bundleManifest.entries || []; } } const site = { name, domain, baseUrl, bundle, routes, size, bytes, files, entries, }; return site; } get def() { return this.manifest.sites[this.index]; } get name() { return (this.def.name || '').trim(); } get domain() { return this.def.domain; } get bundle() { return this.def.bundle; } get bundleUrl() { const base = util.stripSlashes(this.def.baseUrl); const path = util.stripSlashes(this.bundle); return `${base}/${path}`; } get version() { return util.firstSemver(this.bundle) || '0.0.0'; } get size() { return this.def.size; } get files() { return this.def.files || []; } get routes() { if (!this._routes) { const site = this.def; const routes = Object.keys(this.def.routes).map(key => site.routes[key]); this._routes = routes.map(route => Route.create({ site, route })); } return this._routes; } isMatch(domain) { const isMatch = (domain, regex) => { const res = regex.exec(domain); return Array.isArray(res) && res[0] === domain; }; const domains = Array.isArray(domain) ? domain : [domain]; const regexes = this._regexes; return domains.some(d => this.domain.includes(d) || regexes.some(r => isMatch(d, r))); } route(pathname) { return pathname ? this.routes.find(route => route.isMatch(pathname)) : undefined; } redirectUrl(pathname) { const path = util.stripSlashes(pathname); const file = this.files.find(file => path.endsWith(file.path)); return file ? `${this.bundleUrl}/${file.path}` : ''; } toObject() { return Object.assign({}, this.def); } } Site.create = (args) => new Site(args); export function toDomainRegexes(domains) { const toRegex = (domain) => new RegExp(util.stripSlashes(domain)); return domains.filter(domain => util.isDomainRegex(domain)).map(domain => toRegex(domain)); }