1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 | import type PuppeteerUtil from '../injected/injected.js';
|
8 | import {assert} from '../util/assert.js';
|
9 | import {interpolateFunction, stringifyFunction} from '../util/Function.js';
|
10 |
|
11 | import {
|
12 | QueryHandler,
|
13 | type QuerySelector,
|
14 | type QuerySelectorAll,
|
15 | } from './QueryHandler.js';
|
16 | import {scriptInjector} from './ScriptInjector.js';
|
17 |
|
18 |
|
19 |
|
20 |
|
21 | export interface CustomQueryHandler {
|
22 | |
23 |
|
24 |
|
25 | queryOne?: (node: Node, selector: string) => Node | null;
|
26 | |
27 |
|
28 |
|
29 | queryAll?: (node: Node, selector: string) => Iterable<Node>;
|
30 | }
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 | export class CustomQueryHandlerRegistry {
|
45 | #handlers = new Map<
|
46 | string,
|
47 | [registerScript: string, Handler: typeof QueryHandler]
|
48 | >();
|
49 |
|
50 | get(name: string): typeof QueryHandler | undefined {
|
51 | const handler = this.#handlers.get(name);
|
52 | return handler ? handler[1] : undefined;
|
53 | }
|
54 |
|
55 | |
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 |
|
70 |
|
71 |
|
72 |
|
73 |
|
74 | register(name: string, handler: CustomQueryHandler): void {
|
75 | assert(
|
76 | !this.#handlers.has(name),
|
77 | `Cannot register over existing handler: ${name}`,
|
78 | );
|
79 | assert(
|
80 | /^[a-zA-Z]+$/.test(name),
|
81 | `Custom query handler names may only contain [a-zA-Z]`,
|
82 | );
|
83 | assert(
|
84 | handler.queryAll || handler.queryOne,
|
85 | `At least one query method must be implemented.`,
|
86 | );
|
87 |
|
88 | const Handler = class extends QueryHandler {
|
89 | static override querySelectorAll: QuerySelectorAll = interpolateFunction(
|
90 | (node, selector, PuppeteerUtil) => {
|
91 | return PuppeteerUtil.customQuerySelectors
|
92 | .get(PLACEHOLDER('name'))!
|
93 | .querySelectorAll(node, selector);
|
94 | },
|
95 | {name: JSON.stringify(name)},
|
96 | );
|
97 | static override querySelector: QuerySelector = interpolateFunction(
|
98 | (node, selector, PuppeteerUtil) => {
|
99 | return PuppeteerUtil.customQuerySelectors
|
100 | .get(PLACEHOLDER('name'))!
|
101 | .querySelector(node, selector);
|
102 | },
|
103 | {name: JSON.stringify(name)},
|
104 | );
|
105 | };
|
106 | const registerScript = interpolateFunction(
|
107 | (PuppeteerUtil: PuppeteerUtil) => {
|
108 | PuppeteerUtil.customQuerySelectors.register(PLACEHOLDER('name'), {
|
109 | queryAll: PLACEHOLDER('queryAll'),
|
110 | queryOne: PLACEHOLDER('queryOne'),
|
111 | });
|
112 | },
|
113 | {
|
114 | name: JSON.stringify(name),
|
115 | queryAll: handler.queryAll
|
116 | ? stringifyFunction(handler.queryAll)
|
117 | : String(undefined),
|
118 | queryOne: handler.queryOne
|
119 | ? stringifyFunction(handler.queryOne)
|
120 | : String(undefined),
|
121 | },
|
122 | ).toString();
|
123 |
|
124 | this.#handlers.set(name, [registerScript, Handler]);
|
125 | scriptInjector.append(registerScript);
|
126 | }
|
127 |
|
128 | |
129 |
|
130 |
|
131 |
|
132 |
|
133 |
|
134 | unregister(name: string): void {
|
135 | const handler = this.#handlers.get(name);
|
136 | if (!handler) {
|
137 | throw new Error(`Cannot unregister unknown handler: ${name}`);
|
138 | }
|
139 | scriptInjector.pop(handler[0]);
|
140 | this.#handlers.delete(name);
|
141 | }
|
142 |
|
143 | |
144 |
|
145 |
|
146 | names(): string[] {
|
147 | return [...this.#handlers.keys()];
|
148 | }
|
149 |
|
150 | |
151 |
|
152 |
|
153 | clear(): void {
|
154 | for (const [registerScript] of this.#handlers) {
|
155 | scriptInjector.pop(registerScript);
|
156 | }
|
157 | this.#handlers.clear();
|
158 | }
|
159 | }
|
160 |
|
161 |
|
162 |
|
163 |
|
164 | export const customQueryHandlers = new CustomQueryHandlerRegistry();
|