diff --git a/components/Footer.js b/components/Footer.js index b3fa15c..6dc4595 100644 --- a/components/Footer.js +++ b/components/Footer.js @@ -40,8 +40,7 @@ const styles = css` ` export async function Footer (ctx, { path, title }) { - ctx.css.add(styles) - ctx.body = await body(ctx, { path, title }) - + ctx.addCSS(styles) + ctx.setBody(await body(ctx, { path, title })) return ctx } diff --git a/components/Header.js b/components/Header.js index 7c3bfd6..9076a46 100644 --- a/components/Header.js +++ b/components/Header.js @@ -5,7 +5,7 @@ import base from '../styles/base.js' const body = async (ctx, { path, title }) => html`

Title: ${sanitize(title)}

- ${await ctx.b(NavBar, { path })} + ${await ctx.renderBody(NavBar, { path })}
` @@ -17,8 +17,8 @@ const styles = css` ` export async function Header (ctx, { path, title }) { - ctx.css.add(styles) - ctx.body = await body(ctx, { path, title }) + ctx.addCSS(styles) + ctx.setBody(await body(ctx, { path, title })) return ctx } diff --git a/components/NavBar.js b/components/NavBar.js index 2875c21..074e4cc 100644 --- a/components/NavBar.js +++ b/components/NavBar.js @@ -52,7 +52,7 @@ const styles = css` ` export function NavBar (ctx, { path }) { - ctx.css.add(styles) - ctx.body = body(path) + ctx.addCSS(styles) + ctx.setBody(body(path)) return ctx } diff --git a/pages/About.js b/pages/About.js index 176d7e9..8c5bbf8 100644 --- a/pages/About.js +++ b/pages/About.js @@ -8,7 +8,7 @@ const head = ({ title }) => html` ` const body = async (ctx, { path, title }) => html` - ${await ctx.b(Header, { path, title })} + ${await ctx.renderBody(Header, { path, title })}

About Page - ${sanitize(title)}

@@ -23,7 +23,7 @@ const body = async (ctx, { path, title }) => html`
- ${await ctx.b(Footer, { path, title })} + ${await ctx.renderBody(Footer, { path, title })} ` const styles = css` @@ -33,10 +33,10 @@ const styles = css` ` export async function AboutPage (ctx, { path, title }) { - ctx.css.add(base) - ctx.css.add(styles) - ctx.head.add(head({ title })) - ctx.body = await body(ctx, { path, title }) + ctx.addCSS(base) + ctx.addCSS(styles) + ctx.addHead(head({ title })) + ctx.setBody(await body(ctx, { path, title })) return ctx } diff --git a/pages/Home.js b/pages/Home.js index fdce832..cf9a8c3 100644 --- a/pages/Home.js +++ b/pages/Home.js @@ -8,7 +8,7 @@ const head = ({ title }) => html` ` const body = async (ctx, { path, title }) => html` - ${await ctx.b(Header, { path, title })} + ${await ctx.renderBody(Header, { path, title })}

Home Page - ${sanitize(title)}

@@ -17,7 +17,7 @@ const body = async (ctx, { path, title }) => html`

- ${await ctx.b(Footer, { path, title })} + ${await ctx.renderBody(Footer, { path, title })} ` const styles = css` @@ -27,10 +27,10 @@ const styles = css` ` export async function HomePage (ctx, { path, title }) { - ctx.css.add(base) - ctx.css.add(styles) - ctx.head.add(head({ title })) - ctx.body = await body(ctx, { path, title }) + ctx.addCSS(base) + ctx.addCSS(styles) + ctx.addHead(head({ title })) + ctx.setBody(await body(ctx, { path, title })) return ctx } diff --git a/pages/Page.js b/pages/Page.js index bd40e5f..d97f1ac 100644 --- a/pages/Page.js +++ b/pages/Page.js @@ -1,7 +1,7 @@ import { html, render } from '../templater.js' async function Root (ctx, component, ...params) { - const result = await ctx.render(component, ...params) + const result = await render(ctx, component, ...params) const scripts = Array.from(result.js).join(';\n') const css = Array.from(result.css).join('\n') @@ -25,5 +25,5 @@ async function Root (ctx, component, ...params) { } export function page(component, ...params) { - return render(Root, component, ...params) + return render(null, Root, component, ...params) } diff --git a/templater.js b/templater.js index 470d0e5..f83ba81 100644 --- a/templater.js +++ b/templater.js @@ -13,29 +13,46 @@ export const css = tmpl export const javascript = tmpl // Stupid-simple "sanitization". Just enough for this demo, not for production. -export function sanitize(str) { +export function sanitize (str) { if (typeof str !== 'string') return str return str.split('<').join('<') } -// Shorthand to render a component and retrieve the body. -async function b(component, ...params) { - return (await this.render(component, ...params)).body -} +function contextFactory (parentCtx) { + const head = new Set() + const css = new Set() + const js = new Set() + let body = '' -// Shorthand to render a component and retrieve the head. -async function h(component, ...params) { - return (await this.render(component, ...params)).head -} + const addHead = str => { + head.add(str) + if (parentCtx) parentCtx.addHead(str) + } + const addCSS = str => { + css.add(str) + if (parentCtx) parentCtx.addCSS(str) + } + const addJS = str => { + js.add(str) + if (parentCtx) parentCtx.addJS(str) + } + const setBody = str => (body = str) + const renderBody = async (component, ...params) => { + debugger; + (await render(parentCtx, component, ...params)).body + } -// Shorthand to render a component and retrieve the css. -async function c(component, ...params) { - return (await this.render(component, ...params)).css -} - -// Shorthand to render a component and retrieve the js. -async function j(component, ...params) { - return (await this.render(component, ...params)).js + return { + head, + css, + js, + body, + renderBody, + addHead, + addCSS, + addJS, + setBody + } } /** @@ -45,22 +62,9 @@ async function j(component, ...params) { * @returns {Object} * An object containing head elements, css, js, and body text returned by the rendered component. */ -export async function render(component, ...params) { +export async function render (parentCtx, component, ...params) { // Create a new context to pass to a component for each rendering. - const ctx = { - head: new Set(), - css: new Set(), - js: new Set(), - body: null, - } - // Add render functions to the context object, bound to the current context. - // This ensures any calls to render child components will have the parent context bound as "this", allowing copying - // child render results into parent context without having to pass the parent context explicitly to every child component. - ctx.render = render.bind(ctx) - ctx.b = b.bind(ctx) - ctx.h = h.bind(ctx) - ctx.c = c.bind(ctx) - ctx.j = j.bind(ctx) + const ctx = contextFactory(parentCtx) // Attempt to load result from cache by component name and params. const key = hash([component.name, params]) @@ -74,14 +78,6 @@ export async function render(component, ...params) { // Store result to cache if it wasn't already present. if (!stored) toCache(key, result) - // Results with a parent context will copy child context fields into parent context. - // This enables straightforward component-level caching of body, head, css, and js at the cost of higher memory usage. - if (this) { - result.head.forEach(r => this.head.add(r)) - result.css.forEach(r => this.css.add(r)) - result.js.forEach(r => this.js.add(r)) - } - // Return result context object to caller. return result }