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.
$ npm install --save @mjstahl/boring
const { render, renderFile } = require('@mjstahl/boring')
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
.
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.
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!'
})
})
const { render } = require('@mjstahl/boring')
await render('<p>High ${howMany}!</p>', {
howMany: 5
})
/**
<p>High 5!</p
*/
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>
*/
const { render } = require('@mjstahl/boring')
await render('<input disabled=${disabled} autofocus=${focus} />', {
disabled: true,
focus: false
})
/**
<input disabled="disabled" >
*/
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>
*/
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>
*/
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>
*/
Boring was made possible by the work previously done by the choojs/nanohtml contributors.