Skip to content

Lcfvs/dom-engine

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

67 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@lcf.vs/dom-engine

A composable DOM based template engine

Install

npm i @lcf.vs/dom-engine

Usage

Markers

There is 2 type of markers:

  • Required: {name}
    • The value can't be nullish
  • Optional: {?name}
    • If a nullish value is provided for an attribute, that attribute is removed

Create a fragment template

import { template } from '@lcf.vs/dom-engine'

const pTemplate = template(`<p class="{?classes}">{salutations} {?name}</p>`, {
  classes: null,
  salutations: null,
  name: null
})

Create a fragment template, using the source symbol

import { source } from '@lcf.vs/dom-engine'

const pTemplate = {
  [source]: '<p class="{?classes}">{salutations} {?name}</p>',
  classes: null,
  salutations: null,
  name: null
}

Create a value filled node

// object representing <p>hello user-name</p>
const p = {
  ...pTemplate,
  salutations: 'hello',
  name: 'user-name'
}

// object representing <p>hello</p>
const p = {
  ...pTemplate,
  salutations: 'hello'
}

// Error: missing {salutations} (on serialization)
const p = {
  ...pTemplate
}

Use templates as value

const taggedName = {
  [source]: '<strong>{name}</strong>',
  name: null
}

// object representing <p>hello <string>user-name</strong></p>
const p = {
  ...pTemplate,
  salutations: 'hello',
  name: {
    ...taggedName,
    name: 'user-name'
  }
}

// object representing <p>hello <strong>user-name</strong></p>
const p = {
  ...pTemplate,
  salutations: 'hello',
  name: {
    ...taggedName,
    name: 'user-name'
  }
}
// object representing <p>hello <strong><span>user-name</span></strong></p>
const p = {
  ...pTemplate,
  salutations: 'hello',
  name: template(`<span>user-name</span>`)
}
// object representing <p>hello <strong><span>user</span>-<span>name</span></strong></p>
const p = {
  ...pTemplate,
  salutations: 'hello',
  name: [
    template(`<span>user</span>`),
    template(`-`),
    template(`<span>name</span>`)
  ]
}

Use object properties

// object representing <p>hello <strong>user</strong></p>
const pTemplate = {
  [source]: '<p>{salutations} <strong>{user.name}</strong></p>',
  salutations: 'hello',
  user: {
    name: 'user'
  }
}

Serialize a template

import { serialize } from '@lcf.vs/dom-engine'

const p = template(`<p>user-name</p>`)
const html = await serialize(p)

APIs

import { template, source } from '@lcf.vs/dom-engine'

const tpl = template(html, { ...fields } = {})

// or 

const tpl = {
  [source]: html,
  ...fields
}

Back-End

import { load } from '@lcf.vs/dom-engine'

// loads a file with the same name than the current module,
// but with a .html extension instead of the current one
const tpl = await load(import.meta, { ...options } = {}, { ...data } = {})

Where the options are the async readFile() ones

import { serialize } from '@lcf.vs/dom-engine'

const htmlString = await serialize(template)

Or

import { render } from '@lcf.vs/dom-engine'

const htmlNode = render(template)

Front-End

import { load } from '@lcf.vs/dom-engine'

// loads a file with the same name than the current module,
// but with a .html extension instead of the current one
const tpl = await load(import.meta, { ...options } = {}, { ...data } = {})

Where the options are the fetch() ones

import { serialize } from '@lcf.vs/dom-engine'

const htmlString = await serialize(template)

Or

import { render } from '@lcf.vs/dom-engine'

const htmlNode = render(template)

ServiceWorker

The engine also supports the rendering of some templates stored into a ServiceWorker.

To use it, you need to add the following components.

Into the window script

import '@lcf.vs/dom-engine/lib/worker-client.js'

Into the ServiceWorker script

import { load } from '@lcf.vs/dom-engine'

// loads a file with the same name than the current module,
// but with a .html extension instead of the current one
const tpl = await load(import.meta, { ...optio*ns } = {}, { ...data } = {})

Where the options are the fetch() ones

import { serialize, source } from '@lcf.vs/dom-engine'

const text = 'This page is built by sw-template'

const view = {
  [source]: `<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>{text}</title>
  <script src="/main.js" type="module"></script>
</head>
<body>
  <main>
    <h1>{text}</h1>
    <a href="/">back to home</a>
  </main>
</body>
</html>`
}

const html = await serialize({
  ...view,
  text
})

Just note that: if the browser doesn't support the ES6 modules, into the ServiceWorker, you should need to bundle your code.

A very basic demo

Security

To improve your client-side security, you can add the following header on your requests (or via a <meta>)

Content-Security-Policy: default-src 'none'; connect-src 'self'; script-src 'self'; style-src 'self'; require-trusted-types-for 'script'; trusted-types dom-engine

Since the templates filling is intended to avoid XSS injections, by design, you need to be sure about your templates.

Of course, it's just a minimal one, you can adapt it on your needs.

License

MIT