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
 
x31
 
 
 
x31
 
 
x31
x31
x31
x31
x31
x31
x31
x31
x31
 
 
x31
x31
x31
x31
x31
x31
x31
x7247
x7247
x8195
 
x8195
x8195
x4517
x4517
 
x8195
 
x689
x8
x8
x8
 
x689
x689
x8195
x8195
 
x2989
x35
x35
x35
x35
 
x2968
x2637
x2637
x2637
 
x1375
x115
x115
x115
 
x1317
x24
x24
 
x1173
x6
x6
x2989
x8195
x7241
x7247
x31
 
 
 
 
 
x31
x31
x744
 
 
x31



















































































// 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