UNPKG

@shopify/react-html

Version:

A component to render your react app with no static HTML.

123 lines (100 loc) 3.1 kB
import {EffectKind} from '@shopify/react-effect'; import {getSerializationsFromDocument} from './utilities'; interface Title { title: string; } export interface State { title?: string; metas: React.HTMLProps<HTMLMetaElement>[]; links: React.HTMLProps<HTMLLinkElement>[]; bodyAttributes: React.HTMLProps<HTMLBodyElement>; htmlAttributes: React.HtmlHTMLAttributes<HTMLHtmlElement>; } interface Subscription { (state: State): void; } export const EFFECT_ID = Symbol('html'); export class HtmlManager { effect: EffectKind = { id: EFFECT_ID, betweenEachPass: () => this.reset(), }; private serializations = getSerializationsFromDocument(); private titles: Title[] = []; private metas: React.HTMLProps<HTMLMetaElement>[] = []; private links: React.HTMLProps<HTMLLinkElement>[] = []; private htmlAttributes: React.HtmlHTMLAttributes<HTMLHtmlElement>[] = []; private bodyAttributes: React.HTMLProps<HTMLBodyElement>[] = []; private subscriptions = new Set<Subscription>(); get state(): State { const lastTitle = this.titles[this.titles.length - 1]; return { title: lastTitle && lastTitle.title, metas: this.metas, links: this.links, bodyAttributes: Object.assign({}, ...this.bodyAttributes), htmlAttributes: Object.assign({}, ...this.htmlAttributes), }; } reset({includeSerializations = false} = {}) { this.titles = []; this.metas = []; this.links = []; this.subscriptions.clear(); if (includeSerializations) { this.serializations.clear(); } } subscribe(subscription: Subscription) { this.subscriptions.add(subscription); return () => { this.subscriptions.delete(subscription); }; } addTitle(title: string) { return this.addDescriptor({title}, this.titles); } addMeta(meta: React.HTMLProps<HTMLMetaElement>) { return this.addDescriptor(meta, this.metas); } addLink(link: React.HTMLProps<HTMLLinkElement>) { return this.addDescriptor(link, this.links); } addHtmlAttributes(attributes: React.HtmlHTMLAttributes<HTMLHtmlElement>) { return this.addDescriptor(attributes, this.htmlAttributes); } addBodyAttributes(attributes: React.HTMLProps<HTMLBodyElement>) { return this.addDescriptor(attributes, this.bodyAttributes); } setSerialization(id: string, data: unknown) { this.serializations.set(id, data); } getSerialization<T>(id: string): T | undefined { return this.serializations.get(id) as T | undefined; } extract() { return { ...this.state, serializations: [...this.serializations.entries()].map(([id, data]) => ({ id, data, })), }; } private addDescriptor<T>(item: T, list: T[]) { list.push(item); this.updateSubscriptions(); return () => { const index = list.indexOf(item); if (index >= 0) { list.splice(index, 1); this.updateSubscriptions(); } }; } private updateSubscriptions() { for (const subscription of this.subscriptions) { subscription(this.state); } } }