All files / mustache / mod.ts

100.00% Branches 22/22
100.00% Functions 3/3
100.00% Lines 46/46
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
 
x8
x8
 
 
 
x8
x8
x8
x8
x8
x20
x8
x8
x63
x2
x2
x8
x8
x14
x1
x1
x13
x13
x17
x2
x2
x15
x17
x10
x10
x15
x15
x15
x15
x15
 
x17
x10
x10
x10
x10
x14
x17
x1
x1
x13
 
x13
x14
x8
 
 
 
 
x8





















































// Imports
import { type Cache, type Directive, Phase } from "@mizu/internal/engine"
import { capture } from "./capture.ts"
export type * from "@mizu/internal/engine"

/** `*mustache` directive. */
export const _mustache = {
  name: "*mustache",
  phase: Phase.CONTENT_INTERPOLATION,
  multiple: true,
  init(renderer) {
    renderer.cache<Cache<typeof _mustache>>(this.name, new WeakMap())
  },
  setup(renderer, _, { state }) {
    if (state[renderer.internal("mustaching")]) {
      return { execute: true }
    }
  },
  async execute(renderer, element, { cache, ...options }) {
    if (!renderer.isHtmlElement(element)) {
      return
    }
    await Promise.allSettled(
      Array.from(element.childNodes).map(async (node) => {
        if (node.nodeType !== renderer.window.Node.TEXT_NODE) {
          return
        }
        const text = node as Text
        if (!cache.has(text)) {
          cache.set(text, text.textContent!)
        }
        try {
          const template = cache.get(text)!
          let templated = template
          let offset = 0
          let captured = null as ReturnType<typeof capture>
          // deno-lint-ignore no-cond-assign
          while (captured = capture(template, offset)) {
            const { b, match, captured: expression } = captured
            templated = templated.replace(match, `${await renderer.evaluate(element, expression, options).catch((error) => (renderer.warn(error, element), null)) ?? ""}`)
            offset = b
          }
          text.textContent = templated
        } catch (error) {
          renderer.warn(`${error}`.split("\n")[0], element)
        }
      }),
    )
    return { state: { [renderer.internal("mustaching")]: true } }
  },
} as const satisfies Directive<{
  Cache: WeakMap<Text, string>
}>

/** Default exports. */
export default _mustache