summaryrefslogtreecommitdiffstats
path: root/src/server/template/vdom.ts
blob: 899565bc508d5c3781f092493e5d5c5dca1a189e (plain) (blame)
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
export type component<
	T extends Attribute = Attribute,
	U extends node[] = node[],
	R extends node = node
> = ((attr: T, ...content: U) => R)

export interface Attribute {
	[key: string]: unknown,
}

export interface element<T extends string = string> {
	name: T,
	attr: Attribute,
	content: node[],
}

export function iselement<T extends string = string>(e: unknown): e is element<T> {
	return typeof e === "object" && e !== null && "name" in e;
}

export interface typed_data<T extends string = string> {
	content: string,
	type: T
}

export function istyped<T extends string = string>(e: unknown): e is typed_data<T> {
	return typeof e === "object" && e !== null && "type" in e;
}

export type node = element | typed_data | node[] | string

export function c<
	T extends Attribute = Attribute,
	U extends node[] = node[],
	R extends node = node
>(name: component<T, U, R>, attr: T, ...content: U): R {
	return name(attr, ...content)
}

export default function t<T extends string = string>(name: T, attr?: Attribute, ...content: node[]): element<T> {
	return {
		name,
		attr: attr ?? ({} as Attribute),
		content
	}
}

export function frag<T extends node[] = node[]>(_attr: Attribute, ...content: T) {
	return content
}

export function join({sep = ""}: Attribute & {sep?: string}, ...content: string[]) {
	return content.join(sep)
}

export function prenderdata<T extends string = string>(type: T) {
	return function (content: string): typed_data<T> {
		return {
			content,
			type
		}
	}
}

export function render<T extends string = string>(type: T, rendFunc: (node: element, content: string) => string) {
	const data = prenderdata(type)
	return function rend(Node: node): typed_data<T> {
		if (Array.isArray(Node)) {
			return data(Node.map(element => rend(element).content ?? "").join(""))
		}

		if (typeof Node == "string") {
			return data(Node)
		}

		if (iselement(Node)) {
			return data(rendFunc(Node, rend(Node.content).content))
		}

		if (istyped<T>(Node)) {
			if (Node.type == type) {
				return Node
			} else {
				throw new Error(Node.type + "is not valid. The type must be:" + type)
			}
		}
		throw new TypeError("" + Node)
	}
}

export function getText(node: node): string {
	if (Array.isArray(node)) {
		return node.map(element => getText(element) ?? "").join("")
	}

	if (typeof node == "string") {
		return node
	}

	if (iselement(node)) {
		return getText(node.content)
	}

	throw new TypeError("" + node)
}