-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathindex.ts
106 lines (95 loc) · 2.96 KB
/
index.ts
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
import { TextEncoder, TextDecoder } from "util";
import { Macro } from "ava";
import {
Comment,
DocumentHandlers,
Element,
ElementHandlers,
HTMLRewriter as RawHTMLRewriter,
HTMLRewriterOptions as RawHTMLRewriterOptions,
TextChunk,
} from "..";
const encoder = new TextEncoder();
const decoder = new TextDecoder();
export class HTMLRewriter {
private elementHandlers: [selector: string, handlers: ElementHandlers][] = [];
private documentHandlers: DocumentHandlers[] = [];
constructor(private readonly options?: RawHTMLRewriterOptions) {}
on(selector: string, handlers: ElementHandlers): this {
this.elementHandlers.push([selector, handlers]);
return this;
}
onDocument(handlers: DocumentHandlers): this {
this.documentHandlers.push(handlers);
return this;
}
async transform(input: string): Promise<string> {
let output = "";
const rewriter = new RawHTMLRewriter((chunk) => {
output += decoder.decode(chunk);
}, this.options);
for (const [selector, handlers] of this.elementHandlers) {
rewriter.on(selector, handlers);
}
for (const handlers of this.documentHandlers) {
rewriter.onDocument(handlers);
}
try {
await rewriter.write(encoder.encode(input));
await rewriter.end();
return output;
} finally {
rewriter.free();
}
}
}
export function wait(t: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, t));
}
export const mutationsMacro: Macro<
[
(
rw: HTMLRewriter,
handler: (token: Element | TextChunk | Comment) => void
) => HTMLRewriter,
string,
{
beforeAfter: string;
replace: string;
replaceHtml: string;
remove: string;
}
]
> = async (t, func, input, expected) => {
// In all these tests, only process text chunks containing text. All test
// inputs for text handlers will be single characters, so we'll only process
// text nodes once.
// before/after
let res = await func(new HTMLRewriter(), (token) => {
if ("text" in token && !token.text) return;
token.before("<span>before</span>");
token.before("<span>before html</span>", { html: true });
token.after("<span>after</span>");
token.after("<span>after html</span>", { html: true });
}).transform(input);
t.is(res, expected.beforeAfter);
// replace
res = await func(new HTMLRewriter(), (token) => {
if ("text" in token && !token.text) return;
token.replace("<span>replace</span>");
}).transform(input);
t.is(res, expected.replace);
res = await func(new HTMLRewriter(), (token) => {
if ("text" in token && !token.text) return;
token.replace("<span>replace</span>", { html: true });
}).transform(input);
t.is(res, expected.replaceHtml);
// remove
res = await func(new HTMLRewriter(), (token) => {
if ("text" in token && !token.text) return;
t.false(token.removed);
token.remove();
t.true(token.removed);
}).transform(input);
t.is(res, expected.remove);
};