boring
Templating using JavaScript's tagged template strings. Designed for server-side rendering.
There is no magic in this library. This is just JavaScript. This library is boring and that is just the way we like it.
Installation
$ npm install --save @mjstahl/boring
const { render, renderFile } = require('@mjstahl/boring')
External API
render(template: String[, values: Object][, callback: Function]) -> Promise | undefined
Render the template string. If JavaScript expressions exist within the template,
those expressions will be evaluated with regards to the provided values
. If callback
is specified, render
will return undefined
and callback
will be evaluated with the 0th argument set to any error or the 1st argument set to a result. If callback
is not specific the function will return a Promise
.
renderFile(path: String[, values: Object][, callback: Function]) -> Promise | undefined
Render the template located at path
. If JavaScript expressions
exist within the template, those expressions will be evaluated with regards to
the provided values
. If callback
is specified, renderFile
will return undefined
and callback
will be evaluated with the 0th argument set to any error or the 1st argument set to a result. If callback
is not specific the function will return a Promise
.
Template API
include(file: String[, values: Object]) -> Promise
Evaluate a template file located relative to the current template. If JavaScript
expressions exist within the template, those expressions will be evaluated with
regards to the provided values
.
raw(html: String) -> Object
Use raw
in a template where the expression is expected to return HTML. You
do not want to escape HTML twice.
Express 3.x
After installing the boring
package, we set the location of the views and
the engine Express will use to render those views.
const boring = require('@mjstahl/boring')
app.engine('html', boring.renderFile);
app.set('view engine', 'html');
app.set('views', __dirname + '/views');
Next create a file named index.html
in the ./views
directory with the
following content:
<html>
<head>
<title>${title}</title>
</head>
<body>
<h1>${message}</h1>
</body>
</html>
Now we can configure a route that will render the index.html
file.
app.get('/', (req, res) => {
res.render('index', {
title: 'Your first boring template',
message: 'Looking good!'
})
})
Usage
const { render } = require('@mjstahl/boring')
await render('<p>High ${howMany}!</p>', {
howMany: 5
})
/**
<p>High 5!</p
*/
Spread Attributes
const { render } = require('@mjstahl/boring')
const props = { class: 'abc', id: 'def' }
await render('<div ${props}>Hello</div>', { props })
/**
<div class="abc" id="def">Hello</div>
*/
Boolean Attributes
const { render } = require('@mjstahl/boring')
await render('<input disabled=${disabled} autofocus=${focus} />', {
disabled: true,
focus: false
})
/**
<input disabled="disabled" >
*/
List Rendering
Boring will join list items with ''
. So if you want pretty DOM, you have to
handle the whitespace in your template.
<!-- views/states.html -->
<select>
${Object.keys(states).map((s) => {
return `<option value="${s}">${states[s]}</option>`
})}
</select>
const { renderFile } = require('@mjstahl/boring')
const states = { AL: 'Alabama', GA: 'Georgia' }
await renderFile('views/states.html', { states })
/**
<select>
<option value="AL">Alabama</option><option value="GA">Georgia</option>
</select>
*/
Escaped HTML
By default all content inside template strings is escaped. This is great for
strings, but not ideal if you want to insert HTML that's been returned from
another function (for example: a markdown renderer). Use boring/raw
for
interpolating HTML directly.
const { render } = require('@mjstahl/boring')
await render('<body>${raw(header)}</body>', {
header: '<h1>This a regular string</h1>'
})
/**
<body>
<h1>This is a regular string</h1>
</body>
*/
Modularizing Templates
Let's assume we have a views
directory that contained three files: index.html
header.html
, and footer.html
. The contents of each file are as follows:
<!-- views/index.html -->
<html>
<head>
<title>${title}</title>
</head>
<body>
${include('header.html', header)}
<p>This is the body.</p>
${include('footer.html', footer)}
</body>
</html>
<!-- views/header.html -->
<header>
<h1>${title}</h1>
</header>
<!-- views/footer.html -->
<footer>
<p>${year} © ${company}</p>
</footer>
Now we can then render the top-level template passing in values for it and all of its child templates.
const { renderFile } = require('@mjstahl/boring')
await renderFile('views/index.html', {
title: 'Hello Boring!',
header: { title: 'Woohoo Modularity!' },
footer: {
company: 'Nobody Important',
year: 2018
}
})
/**
<html>
<head>
<title>Hello Boring!</title>
</head>
<body>
<header>
<h1>Woohoo Modularity!</h1>
</header>
<p>This is the body.</p>
<footer>
<p>2018 © Nobody Important</p>
</footer>
</body>
</html>
*/
Used By
Attribution
Boring was made possible by the work previously done by the choojs/nanohtml contributors.