All files / if / else / mod.ts

100.00% Branches 31/31
100.00% Functions 1/1
100.00% Lines 37/37
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
 
x8
x8
 
 
 
x8
x8
x8
x8
x8
x50
x50
x50
 
x116
x1
x1
 
 
x116
x116
x116
x30
x30
x30
 
 
x116
x1
x1
 
 
x116
x26
x26
x26
x26
x26
x17
x17
x26
x67
x67
x3
x50
x8
 
 
 
 
x8

















































// Imports
import { type Directive, type Nullable, Phase } from "@mizu/internal/engine"
import { _if, type Cache } from "@mizu/if"
export type * from "@mizu/internal/engine"

/** `*else` directive. */
export const _else = {
  name: "*else",
  phase: Phase.TOGGLE,
  default: "true",
  execute(renderer, element, { attributes: [attribute], context }) {
    const cache = renderer.cache<Cache>(_if.name)
    let previous = element.previousSibling as Nullable<HTMLElement | Comment>
    while (previous) {
      // Break on non-empty text nodes
      if ((previous.nodeType === renderer.window.Node.TEXT_NODE) && (previous.textContent?.trim())) {
        break
      }

      // Force directive to `false` when a previous operand is truthy
      const closing = cache?.templates.get(cache.generated.get(previous)!)?.end === previous
      const opened = Boolean(cache?.templates.get(previous as Comment)?.end.parentNode)
      if (closing || opened || ((renderer.isHtmlElement(previous)) && (renderer.getAttributes(previous, [_if.name, _else.name] as string[], { first: true })))) {
        renderer.depend(element, closing ? cache.generated.get(previous)! : previous, { context })
        return _if.execute(renderer, element, { ...arguments[2], _directive: { directive: this.name, expression: attribute.value, value: "false" } })
      }

      // Break on other elements
      if (renderer.isHtmlElement(previous)) {
        break
      }

      // Execute directive with given expression when first operand is found and is falsy (meaning all previous operand were falsy too)
      if (renderer.isComment(previous)) {
        const original = renderer.cache("*").get(previous)
        if (renderer.getAttributes(original, [_if.name, _else.name] as string[], { first: true })) {
          renderer.depend(element, previous, { context })
        }
        if (renderer.getAttributes(original, _if.name, { first: true })) {
          return _if.execute(renderer, element, { ...arguments[2], _directive: { directive: this.name, expression: attribute.value, value: attribute.value || this.default } })
        }
      }
      previous = previous.previousSibling as Nullable<HTMLElement | Comment>
    }
    renderer.warn(`[${this.name}] must be immediately preceded by another [${_if.name}] or [${_else.name}], ignoring`, element)
  },
} as const satisfies Directive<{
  Default: true
}>

/** Default exports. */
export default [_if, _else]