0.4.3 • Published 6 years ago

@liquid-js/lit-ntml v0.4.3

Weekly downloads
-
License
MIT
Repository
github
Last release
6 years ago

Version Downloads MIT License Code of Conduct

Build Status Dependency Status NSP Status codecov Coverage Status

Lightweight and modern templating for SSR in Node.js, inspired by lit-html.

Table of contents

Features

  • await all tasks including Promises
  • cacheStore: new QuickLru() to use a custom ES6 Map compliant cache instance
  • cacheExpiry: 10e3 to set TTL of a cached item. Defaults to 1 year of TTL.
  • minify: true to minify rendered HTML
  • Compatible for ES Modules (import ntml from 'ntml') and CommonJS (const { ntml } = require('ntml');)
  • Uses parse5 to parse HTML by default
  • Uses pretty to prettify HTML by default
  • 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

  1. Install vscode-lit-html extension.
  2. 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 to false.
    • parseHtml <boolean> If true, parse the HTML with parse5, a HTML compliant parser for Node.js. Defaults to true.
    • parseHtmlFragment <boolean> If true, parse the HTML as fragment, i.e. don't wrap the result in <html><body> tags. Defaults to false.
  • 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:

  1. Avoid using lit-ntml to render the content of a HTML element

    const style = () => `
    body {}
    main {}
    `;
    const main = () => html`<style>${style}</style>`;
  2. 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>`;
  3. Wrap with any valid HTML element

    const style = () => html`
    <style>
      body {}
    
      main {}
    </style>`;

License

MIT License © Rong Sen Ng