All files / test / mod.ts

100.00% Branches 45/45
100.00% Functions 1/1
100.00% Lines 62/62
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
 
x30
 
 
 
x30
 
 
x30
x30
x30
x30
x30
x30
x30
x30
x30
 
 
x30
x30
x30
x30
x30
x30
x30
x5544
x5544
x6106
 
x6106
x6106
x3161
x3161
 
x6106
 
x679
x4
x4
x4
 
x679
x679
x6106
x6106
 
x2266
x32
x32
x32
x32
 
x2247
x1934
x1934
x1934
 
x811
x100
x100
x100
 
x753
x24
x24
 
x609
x5
x5
x2266
x6106
x5539
x5544
x30
 
 
 
 
 
x30
x30
x720
 
 
x30



















































































// Imports
import { type Directive, type NonVoid, Phase } from "@mizu/internal/engine"
export type * from "@mizu/internal/engine"

/** Delta for {@linkcode Phase} to prevent collisions when {@linkcode Directive.multiple} is not enabled. */
export const PHASE_TESTING_DELTA = -0.5 as number

/** `~test` typings. */
export const typings = {
  modifiers: {
    text: { type: Boolean },
    comment: { type: Boolean },
    eval: { type: Boolean },
    throw: { type: Boolean },
    event: { type: String },
  },
} as const

/** `~test` directive. */
export const _test = {
  name: /^~test/,
  prefix: "",
  phase: Phase.TESTING,
  typings,
  multiple: true,
  async execute(renderer, element, { attributes, ...options }) {
    const returned = {} as NonVoid<Awaited<ReturnType<NonNullable<Directive["execute"]>>>>
    for (const attribute of attributes) {
      const parsed = renderer.parseAttribute(attribute, this.typings, { modifiers: true })
      // Skip if tag does not match current phase
      parsed.tag ??= Phase[Phase.TESTING]
      if ((parsed.tag) && (this.phase !== (Phase[parsed.tag.toLocaleUpperCase() as keyof typeof Phase] + PHASE_TESTING_DELTA))) {
        continue
      }
      // Handle comment elements
      if (renderer.isComment(element)) {
        // Uncomment element on falsy `comment` modifier value
        if ((parsed.modifiers.comment) && (!await renderer.evaluate(element, attribute.value || "'true'", options))) {
          returned.element = renderer.uncomment(element)
          element = returned.element
        }
        // Set comment content on any `~test` directive
        renderer.setAttribute(element, attribute.name, attribute.value)
        continue
      } // Handle HTML elements
      else if (renderer.isHtmlElement(element)) {
        // Comment element on truthy `comment` modifier value
        if ((parsed.modifiers.comment) && (await renderer.evaluate(element, attribute.value || "'true'", options))) {
          returned.element = renderer.comment(element, { expression: attribute.value, directive: attribute.name })
          element = returned.element
          continue
        }
        // Set text content on `text` modifier value
        if (parsed.modifiers.text) {
          element.textContent = `${await renderer.evaluate(element, attribute.value, options)}`
          continue
        }
        // Evaluate attribute value on `eval` modifier value
        if (parsed.modifiers.eval) {
          await renderer.evaluate(element, attribute.value, options)
          continue
        }
        // Evaluate attribute value on `event` modifier value
        if (parsed.modifiers.event) {
          element.addEventListener(parsed.modifiers.event, (event) => renderer.evaluate(element, attribute.value, { context: options.context, state: { ...options.state, $event: event }, args: [event] }))
        }
        // Throw error on truthy `throw` modifier value
        if ((parsed.modifiers.throw) && (await renderer.evaluate(element, attribute.value || "'true'", options))) {
          throw new EvalError(`Expected error from: ${element.outerHTML.replace(element.innerHTML, "")}`)
        }
      }
    }
    return returned
  },
} as const satisfies Directive<{
  Name: RegExp
  Typings: typeof typings
}>

/** `~test` directives. */
const _tests = Object.entries(Phase)
  .filter(([, value]) => (Number.isFinite(value)) && (Number(value) > Phase.META))
  .map(([key, value]) => ({ ..._test, name: new RegExp(`${_test.name.source}(?<${key.toLocaleLowerCase()}>)`), phase: (value as number) + PHASE_TESTING_DELTA })) as Array<typeof _test>

/** Default exports. */
export default _tests