import { customize , defaultCompiler } from '@kirei/html' ; import type { TemplateCompiler } from '@kirei/html' ; import { unref , isRef } from '@vue/reactivity' ; import { getCurrentInstance } from './instance' ; import { HookTypes } from '../api/lifecycle' ; import type { ComponentInstance } from '../types' ; import { bind } from '../directives/bind' ; import { conditionalUnless , conditionalIf } from '../directives/conditional' ; import { on } from '../directives/on' ; import { show } from '../directives/show' ; import { model } from '../directives/model' ; /**
 * String of allowed characters to use as a directive alias
 */ const ALIAS_NAMES = '@#&$%*!?;=^¶§€' ; /**
 * Regex to validate a directive alias
 */ const ALIAS_REGEX = new RegExp ( `^([${ ALIAS_NAMES }])([a-z0-9-]*)((?:\\.[a-z0-9-]+)*)$` , 'i' ) ; /**
 * Regex to validate directive names
 */ const DIRECTIVE_REGEX = /^v-([a-z0-9-]+)(?:\:([a-z0-9-]*))?((?:\.[a-z0-9-]+)*)$/i ; /**
 * Directive has a set of lifecycle hooks:
 */ export interface Directive < T = any > { param el param binding beforeMount ? ( el , binding ) : void ; param el param binding mounted ? ( el , binding ) : void ; param el param binding beforeUpdate ? ( el , binding ) : void ; param el param binding updated ? ( el , binding ) : void ; param el param binding beforeUnmount ? ( el , binding ) : void ; param el param binding unmounted ? ( el , binding ) : void ; } export interface DirectiveBinding < T = any > { instance : ComponentInstance ; value : T ; oldValue : T ; arg : string ; modifiers : string [ ] ; dir : Directive < T > ; } param el function refPatcher ( el ) { return ( ref ) => { if ( ! isRef < Element > ( ref ) ) { throw new TypeError ( `Ref directive requires a ref as its expression value` ) ; } ref . value = el ; } ; } /**
 * Custom compiler for directives and to unpack reactives
 */ export const compiler = { attr ( node , attr ) { if ( attr === 'ref' ) { return refPatcher ( node ) ; } const isAlias = ALIAS_NAMES . includes ( attr [ 0 ] ) ; if ( ! isAlias && ! attr . startsWith ( 'v-' ) ) { const patch = defaultCompiler . attr ( node , attr ) ; return ( pending ) => patch ( unref ( pending ) ) ; } const match = attr . match ( isAlias ? ALIAS_REGEX : DIRECTIVE_REGEX ) ; if ( ! match ) { throw new TypeError ( `Invalid directive format '${ attr }'.` ) ; } const name = match [ 1 ] ; const el = node as HTMLElement ; const arg = match [ 2 ] ?? '' ; const modifiers = match [ 3 ] ? match [ 3 ] . slice ( 1 ) . split ( '.' ) : [ ] ; switch ( name ) { case 'bind' : return bind ( el , arg , modifiers ) ; case 'if' : return conditionalIf ( el , arg , modifiers ) ; case 'unless' : return conditionalUnless ( el , arg , modifiers ) ; case 'show' : return show ( el , arg , modifiers ) ; case '@' : case 'on' : return on ( el , arg , modifiers ) ; case '&' : case 'model' : return model ( el , arg , modifiers ) ; } const instance = getCurrentInstance ( ) ; const dir = instance . directives ?. [ name ] ; if ( ! dir ) { throw new TypeError ( `Directive ${ name } not defined.` ) ; } const binding = { instance , modifiers , dir , arg , value : undefined , oldValue : undefined , } ; for ( const key of Object . values ( HookTypes ) ) { if ( key in dir ) { instance . injectHook ( key , dir [ key ] . bind ( null , el , binding ) ) ; } } return ( pending ) => { binding . oldValue = binding . value ; binding . value = pending ; } ; } , node ( ref ) { const patch = defaultCompiler . node ( ref ) ; return ( pending ) => patch ( unref ( pending ) ) ; } , text ( node ) { const patch = defaultCompiler . text ( node ) ; return ( pending ) => patch ( unref ( pending ) ) ; } , } ; export const { html , svg , render } = customize ( { compiler } ) ;