UNPKG

@platform/react.ssr

Version:

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

136 lines (135 loc) 4.72 kB
import { defaultValue, fs, semver, util } from './common'; import { Manifest } from './manifest'; const { stripSlashes } = util; const DEFAULT = { FILE: 'ssr.yml', }; const ERROR = { noFile: (path) => `An "${DEFAULT.FILE}" configuration file does not exist at: ${path}`, }; export class Config { constructor(args) { this.def = args.def; } get secret() { return toValue(this.def.secret); } get builder() { const builder = this.def.builder || {}; builder.bundles = builder.bundles || 'bundles'; builder.entries = builder.entries || ''; return builder; } get s3() { const s3 = this.def.s3 || {}; const path = s3.path || {}; const endpoint = util.stripHttp(s3.endpoint || ''); const cdn = util.stripHttp(s3.cdn || ''); const accessKey = toValue(s3.accessKey); const secret = toValue(s3.secret); const bucket = s3.bucket || ''; const api = { endpoint, cdn, accessKey, secret, bucket, path: { base: stripSlashes(path.base || ''), manifest: stripSlashes(path.manifest || ''), bundles: stripSlashes(path.bundles || ''), }, get config() { return { endpoint, accessKey, secret }; }, get fs() { return fs.s3(api.config); }, async versions(options = {}) { const s3 = api.fs; const prefix = `${api.path.base}/${api.path.bundles}`; const list = s3.list({ bucket, prefix, }); const dirs = (await list.dirs).items.map(({ key }) => ({ key, version: fs.basename(key) })); const versions = semver.sort(dirs.map(item => item.version)); return options.sort === 'DESC' ? versions.reverse() : versions; }, }; return api; } get baseUrl() { const s3 = this.s3; return `https://${s3.cdn || s3.endpoint}/${s3.path.base}`; } get manifest() { const filePath = fs.resolve(this.def.manifest || 'manifest.yml'); const s3 = this.s3; const manifestUrl = `https://${s3.endpoint}/${s3.bucket}/${s3.path.base}/${s3.path.manifest}`; const baseUrl = this.baseUrl; const config = this; const api = { local: { path: filePath, get exists() { return fs.pathExists(filePath); }, async load(args = {}) { const { loadBundleManifest } = args; return Manifest.fromFile({ path: filePath, baseUrl, loadBundleManifest }); }, async ensureLatest(args = {}) { if (!(await api.local.exists)) { await config.createFromTemplate(); } const remote = await api.s3.pull({ force: true }); if (remote.ok) { const minimal = defaultValue(args.minimal, true); await remote.save(filePath, { minimal }); } return api.local.load(); }, }, s3: { url: manifestUrl, async pull(args = {}) { return Manifest.get(Object.assign(Object.assign({}, args), { manifestUrl, baseUrl })); }, }, }; return api; } async createFromTemplate() { const source = fs.join(__dirname, '../tmpl/manifest.yml'); const target = this.manifest.local.path; await fs.ensureDir(fs.dirname(target)); await fs.copy(source, target); return { source, target }; } } Config.create = async (options = {}) => { const path = fs.resolve(options.path || DEFAULT.FILE); if (!(await fs.pathExists(path))) { throw new Error(ERROR.noFile(path)); } else { const def = await fs.file.loadAndParse(path); return new Config({ def }); } }; Config.createSync = (options = {}) => { const path = fs.resolve(options.path || DEFAULT.FILE); if (!fs.pathExistsSync(path)) { throw new Error(`An "ssr.yml" configuration file does not exist at: ${path}`); } else { const def = fs.file.loadAndParseSync(path); return new Config({ def }); } }; const toValue = (value) => { value = value || ''; value = process.env[value] ? process.env[value] : value; return value || ''; };