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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126 |
x6
x6
x6
x6
x6
x6
x6
x6
x6
x6
x6
x6
x6
x6
x6
x6
x6
x6
x6
x6
x19
x6
x6
x18
x1
x1
x17
x18
x2
x2
x18
x3
x3
x1
x1
x3
x17
x18
x2
x2
x18
x3
x3
x17
x17
x18
x13
x13
x17
x13
x1
x13
x13
x11
x11
x11
x11
x11
x3
x3
x11
x17
x6
x6
x113
x113
x3
x3
x1
x1
x3
x3
x3
x2
x2
x3
x5
x1
x1
x5
x5
x4
x102
x4
x4
x4
x3
x3
x113
x2
x2
x2
x113
x6
x6 |
|
import { type Cache, type Directive, Phase } from "@mizu/internal/engine"
export type * from "@mizu/internal/engine"
const spacing = "[\\fnrtvu0020u1680u2000-u200au2028u2029u202fu205fu3000ufeff]".replace(/([g-z])/g, "\\$1")
const regexp = {
condense: new RegExp(`${spacing}+`, "g"),
trim: new RegExp(`(?:^${spacing}*)|(?:${spacing}*$)`, "g"),
clean: new RegExp(`${spacing}*(\\u00a0)${spacing}*`, "g"),
}
export const typings = {
modifiers: {
comments: { type: Boolean },
spaces: { type: Boolean },
directives: { type: Boolean },
templates: { type: Boolean },
},
} as const
export const _clean = {
name: "*clean",
phase: Phase.CONTENT_CLEANING,
typings,
init(renderer) {
renderer.cache<Cache<typeof this>>(this.name, { directives: new WeakSet(), templates: new WeakSet(), comments: new WeakSet() })
},
execute(renderer, element, { attributes: [attribute], cache }) {
if (!renderer.isHtmlElement(element)) {
return
}
const parsed = renderer.parseAttribute(attribute, this.typings, { modifiers: true })
if (parsed.modifiers.templates) {
cache.templates.add(element)
}
if (parsed.modifiers.directives) {
cache.directives.add(element)
if (parsed.modifiers.comments) {
cache.comments.add(element)
}
}
let filter = 0
if (parsed.modifiers.spaces) {
filter |= renderer.window.NodeFilter.SHOW_TEXT
}
if (parsed.modifiers.comments) {
filter |= renderer.window.NodeFilter.SHOW_COMMENT
}
const walker = renderer.document.createTreeWalker(element, filter, { acceptNode: () => renderer.window.NodeFilter.FILTER_ACCEPT })
const nodes = [] as Node[]
while (walker.nextNode()) {
nodes.push(walker.currentNode)
}
nodes.forEach((node) => {
if (parsed.modifiers.comments && (node.nodeType === renderer.window.Node.COMMENT_NODE) && (!renderer.cache("*").has(node as Comment))) {
;(node as Comment).remove()
}
else if (parsed.modifiers.spaces && (node.nodeType === renderer.window.Node.TEXT_NODE)) {
node.textContent = node.textContent!
.replace(regexp.condense, " ")
.replace(regexp.trim, "")
.replace(regexp.clean, "$1")
if (!node.textContent) {
;(node as Text).remove()
}
}
})
},
cleanup(renderer, _element, { cache }) {
const element = _element as HTMLElement
if (cache.directives.has(element)) {
let filter = renderer.window.NodeFilter.SHOW_ELEMENT
if (cache.comments.has(element)) {
filter |= renderer.window.NodeFilter.SHOW_COMMENT
}
const walker = renderer.document.createTreeWalker(element, filter, { acceptNode: () => renderer.window.NodeFilter.FILTER_ACCEPT })
const nodes = [element] as Node[]
while (walker.nextNode()) {
nodes.push(walker.currentNode)
}
nodes.forEach((node) => {
if ((node.nodeType === renderer.window.Node.COMMENT_NODE) && (renderer.cache("*").has(node as Comment))) {
;(node as Comment).remove()
cache.comments.delete(element)
}
else if (node.nodeType === renderer.window.Node.ELEMENT_NODE) {
renderer.directives.forEach((directive) => {
renderer.getAttributes(node as HTMLElement, directive.name).forEach((attribute) => (node as HTMLElement).removeAttributeNode(attribute))
})
cache.directives.delete(element)
}
})
}
if (cache.templates.has(element)) {
Array.from(element.querySelectorAll("template")).forEach((template) => template.remove())
cache.templates.delete(element)
}
},
} as const satisfies Directive<{
Cache: { directives: WeakSet<HTMLElement>; templates: WeakSet<HTMLElement>; comments: WeakSet<HTMLElement> }
Typings: typeof typings
}>
export default _clean
|