Convert context to a factory.
This commit is contained in:
parent
05c575bb26
commit
6f24dd523b
@ -40,8 +40,7 @@ const styles = css`
|
|||||||
`
|
`
|
||||||
|
|
||||||
export async function Footer (ctx, { path, title }) {
|
export async function Footer (ctx, { path, title }) {
|
||||||
ctx.css.add(styles)
|
ctx.addCSS(styles)
|
||||||
ctx.body = await body(ctx, { path, title })
|
ctx.setBody(await body(ctx, { path, title }))
|
||||||
|
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import base from '../styles/base.js'
|
|||||||
const body = async (ctx, { path, title }) => html`
|
const body = async (ctx, { path, title }) => html`
|
||||||
<header>
|
<header>
|
||||||
<h3 style="flex: 1;">Title: ${sanitize(title)}</h3>
|
<h3 style="flex: 1;">Title: ${sanitize(title)}</h3>
|
||||||
${await ctx.b(NavBar, { path })}
|
${await ctx.renderBody(NavBar, { path })}
|
||||||
</header>
|
</header>
|
||||||
`
|
`
|
||||||
|
|
||||||
@ -17,8 +17,8 @@ const styles = css`
|
|||||||
`
|
`
|
||||||
|
|
||||||
export async function Header (ctx, { path, title }) {
|
export async function Header (ctx, { path, title }) {
|
||||||
ctx.css.add(styles)
|
ctx.addCSS(styles)
|
||||||
ctx.body = await body(ctx, { path, title })
|
ctx.setBody(await body(ctx, { path, title }))
|
||||||
|
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ const styles = css`
|
|||||||
`
|
`
|
||||||
|
|
||||||
export function NavBar (ctx, { path }) {
|
export function NavBar (ctx, { path }) {
|
||||||
ctx.css.add(styles)
|
ctx.addCSS(styles)
|
||||||
ctx.body = body(path)
|
ctx.setBody(body(path))
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ const head = ({ title }) => html`
|
|||||||
`
|
`
|
||||||
|
|
||||||
const body = async (ctx, { path, title }) => html`
|
const body = async (ctx, { path, title }) => html`
|
||||||
${await ctx.b(Header, { path, title })}
|
${await ctx.renderBody(Header, { path, title })}
|
||||||
<main id="app">
|
<main id="app">
|
||||||
<section>
|
<section>
|
||||||
<h1>About Page - ${sanitize(title)}</h1>
|
<h1>About Page - ${sanitize(title)}</h1>
|
||||||
@ -23,7 +23,7 @@ const body = async (ctx, { path, title }) => html`
|
|||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
${await ctx.b(Footer, { path, title })}
|
${await ctx.renderBody(Footer, { path, title })}
|
||||||
`
|
`
|
||||||
|
|
||||||
const styles = css`
|
const styles = css`
|
||||||
@ -33,10 +33,10 @@ const styles = css`
|
|||||||
`
|
`
|
||||||
|
|
||||||
export async function AboutPage (ctx, { path, title }) {
|
export async function AboutPage (ctx, { path, title }) {
|
||||||
ctx.css.add(base)
|
ctx.addCSS(base)
|
||||||
ctx.css.add(styles)
|
ctx.addCSS(styles)
|
||||||
ctx.head.add(head({ title }))
|
ctx.addHead(head({ title }))
|
||||||
ctx.body = await body(ctx, { path, title })
|
ctx.setBody(await body(ctx, { path, title }))
|
||||||
|
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ const head = ({ title }) => html`
|
|||||||
`
|
`
|
||||||
|
|
||||||
const body = async (ctx, { path, title }) => html`
|
const body = async (ctx, { path, title }) => html`
|
||||||
${await ctx.b(Header, { path, title })}
|
${await ctx.renderBody(Header, { path, title })}
|
||||||
<main id="app">
|
<main id="app">
|
||||||
<section>
|
<section>
|
||||||
<h1>Home Page - ${sanitize(title)}</h1>
|
<h1>Home Page - ${sanitize(title)}</h1>
|
||||||
@ -17,7 +17,7 @@ const body = async (ctx, { path, title }) => html`
|
|||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
${await ctx.b(Footer, { path, title })}
|
${await ctx.renderBody(Footer, { path, title })}
|
||||||
`
|
`
|
||||||
|
|
||||||
const styles = css`
|
const styles = css`
|
||||||
@ -27,10 +27,10 @@ const styles = css`
|
|||||||
`
|
`
|
||||||
|
|
||||||
export async function HomePage (ctx, { path, title }) {
|
export async function HomePage (ctx, { path, title }) {
|
||||||
ctx.css.add(base)
|
ctx.addCSS(base)
|
||||||
ctx.css.add(styles)
|
ctx.addCSS(styles)
|
||||||
ctx.head.add(head({ title }))
|
ctx.addHead(head({ title }))
|
||||||
ctx.body = await body(ctx, { path, title })
|
ctx.setBody(await body(ctx, { path, title }))
|
||||||
|
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { html, render } from '../templater.js'
|
import { html, render } from '../templater.js'
|
||||||
|
|
||||||
async function Root (ctx, component, ...params) {
|
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 scripts = Array.from(result.js).join(';\n')
|
||||||
const css = Array.from(result.css).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) {
|
export function page(component, ...params) {
|
||||||
return render(Root, component, ...params)
|
return render(null, Root, component, ...params)
|
||||||
}
|
}
|
||||||
|
72
templater.js
72
templater.js
@ -18,24 +18,41 @@ export function sanitize(str) {
|
|||||||
return str.split('<').join('<')
|
return str.split('<').join('<')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shorthand to render a component and retrieve the body.
|
function contextFactory (parentCtx) {
|
||||||
async function b(component, ...params) {
|
const head = new Set()
|
||||||
return (await this.render(component, ...params)).body
|
const css = new Set()
|
||||||
|
const js = new Set()
|
||||||
|
let body = ''
|
||||||
|
|
||||||
|
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 head.
|
return {
|
||||||
async function h(component, ...params) {
|
head,
|
||||||
return (await this.render(component, ...params)).head
|
css,
|
||||||
|
js,
|
||||||
|
body,
|
||||||
|
renderBody,
|
||||||
|
addHead,
|
||||||
|
addCSS,
|
||||||
|
addJS,
|
||||||
|
setBody
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -45,22 +62,9 @@ async function j(component, ...params) {
|
|||||||
* @returns {Object<head: Set, css: Set, js: Set, body: String|null, render: Function>}
|
* @returns {Object<head: Set, css: Set, js: Set, body: String|null, render: Function>}
|
||||||
* An object containing head elements, css, js, and body text returned by the rendered component.
|
* 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.
|
// Create a new context to pass to a component for each rendering.
|
||||||
const ctx = {
|
const ctx = contextFactory(parentCtx)
|
||||||
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)
|
|
||||||
|
|
||||||
// Attempt to load result from cache by component name and params.
|
// Attempt to load result from cache by component name and params.
|
||||||
const key = hash([component.name, 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.
|
// Store result to cache if it wasn't already present.
|
||||||
if (!stored) toCache(key, result)
|
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 context object to caller.
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user