All files / mustache / mod.ts

100.00% Branches 11/11
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
 
x8
x8
 
 
 
x8
x8
x8
x8
x8
x28
x8
x8
x71
x219
x73
x8
x8
x22
x23
x23
x35
x35
x52
x54
x54
x67
x52
x62
x62
x67
x67
x67
x67
x67
 
x52
x62
x62
x62
x62
x66
x52
x53
x53
x35
 
x175
x22
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 Directive<WeakMap<Text, string>> & { default: NonNullable<Directive["default"]> }

/** Default exports. */
export default _mustache