@liquid-js/lit-ntml
v0.4.3
Published
Inspired by lit-html but for Node.js
Downloads
6
Maintainers
Readme
Lightweight and modern templating for SSR in Node.js, inspired by lit-html.
Table of contents
Features
- [x]
await
all tasks including Promises - [x]
cacheStore: new QuickLru()
to use a custom ES6 Map compliant cache instance - [x]
cacheExpiry: 10e3
to set TTL of a cached item. Defaults to 1 year of TTL. - [x]
minify: true
to minify rendered HTML - [x] Compatible for ES Modules (
import ntml from 'ntml'
) and CommonJS (const { ntml } = require('ntml');
) - [x] Uses parse5 to parse HTML by default
- [x] Uses pretty to prettify HTML by default
- [x] Support HTML syntax highlighting + autocompletion with vscode-lit-html in JavaScript's template string.
Pre-requisite
How to use
Install
# Install via NPM
$ npm install lit-ntml
Enable syntax highlighting when writing HTML with template literal
Visual Studio Code
- Install vscode-lit-html extension.
- If the extension does not provide that syntax highlighting and autocompletion, try writing your templates in
.jsx
file (or.tsx
file if you're TypeScript user) . That should work.
Code examples
Await all tasks (Promises, Functions, strings, etc)
/** Import project dependencies */
import ntml from 'lit-ntml';
/** Setting up */
const html = ntml();
const header = text => () => new Promise(yay => setTimeout(() => yay(`<div class="header">${text}</div>`), 3e3));
const content = text => async () => `<div class="content">${text}</div>`;
const someLoremIpsum = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
const rendered = await html`
<html lang="en">
<body>
<style>
body {
padding: 0;
margin: 0;
font-size: 16px;
font-family: 'sans-serif';
box-sizing: border-box;
}
.header {
background: #0070fb;
color: #fff;
}
.content {
background: #f5f5f5;
color: #000;
}
</style>
<main>
<div>Hello, world! ${header('Hello, world!')} ${content('lorem ipsum')}</div>
<div>${someLoremIpsum}</div>
</main>
</body>
</html>
`;
console.log('#', rendered); /** <html lang="en>...</html> */
Use custom cache store + unique cache name to cache rendered HTML
/** Import project dependencies */
import ntml from 'lit-ntml';
import QuickLru from 'quick-lru';
/** Setting up */
const cacheStore = new QuickLru({ maxSize: 1000 }); // A cache instance must be ES6 Map compliant.
// const simpleCache = new Map(); // Simple cache using ES6 Map.
const html = ntml({
cacheStore, // cacheStore: simpleCache,
cacheName: 'main', // Gives the rendered HTML a unique name
cacheExpiry: 10e3, // Set TTL of the rendered HTML. Defaults to 1 year.
});
const cacheAfterRendered = await html`
<html lang="en">
<body>
<style>
body {
padding: 0;
margin: 0;
font-size: 16px;
font-family: 'sans-serif';
box-sizing: border-box;
}
.header {
background: #0070fb;
color: #fff;
}
.content {
background: #f5f5f5;
color: #000;
}
</style>
<main>
<div>Hello, world!</div>
<div>This content will be cached!</div>
</main>
</body>
</html>
`;
console.log('#', cacheAfterRendered); /** <html lang="en">...</html> */
Minify rendered HTML
/** Import project dependencies */
import ntml from 'lit-ntml';
/** Setting up */
const html = ntml({
minify: true,
});
const minifyAfterRendered = await html`
<html lang="en">
<body>
<style>
body {
padding: 0;
margin: 0;
font-size: 16px;
font-family: 'sans-serif';
box-sizing: border-box;
}
.header {
background: #0070fb;
color: #fff;
}
.content {
background: #f5f5f5;
color: #000;
}
</style>
<main>
<div>Hello, world!</div>
<div>This content will be minified!</div>
</main>
</body>
</html>
`;
console.log('#', minifyAfterRendered); /** <html lang="en"><body><style>...</style><main>...</main></body></html> */
Non-TypeScript users
For non-TypeScript users, here's the snippet:
const { ntml } = require('ntml');
(async () => {
const html = ntml();
const rendered = await html`<div>haha</div>`;
console.log('#', rendered);
/**
* <div>haha</div>
*/
})();
API Reference
ntml(options)
options
<Object> Optional configuration for the templating.cacheStore
<Map> Custom ES6 Map compliant cache instance to cache rendered HTML.cacheName
<string> Name of the rendered HTML that needs to be cached. Use a unique name for each rendered HTML to avoid cache conflict.cacheExpiry
<number> How long the rendered HTML should be cached for. Defaults to 1 year (12 * 30 * 24 * 3600
).minify
<boolean> If true, minify rendered HTML. Defaults tofalse
.parseHtml
<boolean> If true, parse the HTML with parse5, a HTML compliant parser for Node.js. Defaults totrue
.parseHtmlFragment
<boolean> If true, parse the HTML as fragment, i.e. don't wrap the result in<html><body>
tags. Defaults tofalse
.
- returns: <Promise<string>> Promise which resolves with rendered HTML.
Caveat
Writing CSS styles outside of HTMLStyleElement can lead to unexpected parsing behavior, such as:
CSS styles outside of <style>
import ntml from 'lit-ntml';
const html = ntml();
const style = () => html`
body {}
div {}
`;
const main = () => html`
<style>${style()}</style>
`;
/**
* <!DOCTYPE>
* <html>
* <head>
* <style>
* <!DOCTYPE html>
* <html>
* <head>
* <style>
* body {}
*
* div {}
* </style>
* </head>
* </html>
* </style>
* </head>
* </html>
*
*/
It's clearly that the style
tag element has been wrapped inside another html
tag element. This is an unexpected behavior. However, it kind of makes sense as from the above scenario each of the new content is rendered separately with lit-ntml
and the lit-ntml
has no knowledge about what will be rendered next and before. To avoid such behavior, do one of the following:
Avoid using
lit-ntml
to render the content of a HTML elementconst style = () => ` body {} main {} `; const main = () => html`<style>${style}</style>`;
Parse style as fragment
const { ntml } = require('lit-ntml'); const fhtml = ntml({ parseHtmlFragment: true, }); const style = () => fhtml` body {} main {} `; const main = () => html`<style>${style}</style>`;
Wrap with any valid HTML element
const style = () => html` <style> body {} main {} </style>`;
License
MIT License © Rong Sen Ng