-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.mjs
92 lines (77 loc) · 2.01 KB
/
main.mjs
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
import {createNode} from './node.mjs'
import {parseTag} from './parse-tag.mjs'
import {parseAttribute} from './parse-attribute.mjs'
import {parseTextNode} from './parse-textnode.mjs'
import {parseComment} from './parse-comment.mjs'
let uid = 0
const parseDoctype = state => {
/// Handles a leading <!doctype ...> declaration.
/// => undefined
if (/^<!doctype[\s>]/i.test(state.src)) {
const node = createNode(state)
node.nodeName = '!DOCTYPE'
node.parent = state.hostNode
state.hostNode.childNodes.push(node)
state.hostNode = node
state.pos += 10
parseAttribute(state)
state.hostNode = node.parent
state.pos++
}
}
const parse = (src, embeddedFragments) => {
/// Transforms the given source into an AST.
/// => AstNode
const ln = src.length
const state = {
src,
embeddedFragments,
ast: null,
hostNode: null,
pos: 0,
end: () => (state.pos === ln),
peek: distance => {
if (!distance || distance === 1) {
return state.src[state.pos]
}
return state.src.slice(state.pos, state.pos + distance)
}
}
state.ast = createNode(state)
state.ast.nodeName = 'FRAGMENT'
state.hostNode = state.ast
parseDoctype(state)
while (!state.end()) {
parseTextNode(state)
parseComment(state)
parseTag(state)
}
return state.ast
}
const serialize = (value, embeddedFragments) => {
/// Expresses the given value as a string.
/// => string
if (value == null) {
return ''
}
if (value.constructor.name === 'AstNode') {
embeddedFragments[++uid] = value
return `<embedded-fragment uid="${uid}">`
}
if (value.constructor === Array) {
return value.map(v => serialize(v, embeddedFragments)).join('')
}
return value.toString()
}
export const dom = (strings, ...tags) => {
/// Produces a virtual DOM object from the given strings and tags.
/// => AstNode - the fragment root node
let src = ''
const embeddedFragments = {}
strings.forEach((str, i) => (
src += (str + serialize(tags[i], embeddedFragments))
))
const fragment = parse(src.trim(), embeddedFragments)
uid = 0
return fragment
}