portadom 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +202 -0
  2. package/dist/cjs/dom/dom.d.ts +19 -0
  3. package/dist/cjs/dom/dom.js +813 -0
  4. package/dist/cjs/dom/dom.js.map +1 -0
  5. package/dist/cjs/dom/domUtils.d.ts +42 -0
  6. package/dist/cjs/dom/domUtils.js +126 -0
  7. package/dist/cjs/dom/domUtils.js.map +1 -0
  8. package/dist/cjs/dom/types.d.ts +371 -0
  9. package/dist/cjs/dom/types.js +216 -0
  10. package/dist/cjs/dom/types.js.map +1 -0
  11. package/dist/cjs/index.d.ts +6 -0
  12. package/dist/cjs/index.js +22 -0
  13. package/dist/cjs/index.js.map +1 -0
  14. package/dist/cjs/page/page.d.ts +12 -0
  15. package/dist/cjs/page/page.js +105 -0
  16. package/dist/cjs/page/page.js.map +1 -0
  17. package/dist/cjs/page/pageUtils.d.ts +16 -0
  18. package/dist/cjs/page/pageUtils.js +116 -0
  19. package/dist/cjs/page/pageUtils.js.map +1 -0
  20. package/dist/cjs/page/types.d.ts +61 -0
  21. package/dist/cjs/page/types.js +3 -0
  22. package/dist/cjs/page/types.js.map +1 -0
  23. package/dist/cjs/utils/async.d.ts +19 -0
  24. package/dist/cjs/utils/async.js +74 -0
  25. package/dist/cjs/utils/async.js.map +1 -0
  26. package/dist/cjs/utils/error.d.ts +1 -0
  27. package/dist/cjs/utils/error.js +10 -0
  28. package/dist/cjs/utils/error.js.map +1 -0
  29. package/dist/cjs/utils/format.d.ts +9 -0
  30. package/dist/cjs/utils/format.js +19 -0
  31. package/dist/cjs/utils/format.js.map +1 -0
  32. package/dist/cjs/utils/types.d.ts +6 -0
  33. package/dist/cjs/utils/types.js +9 -0
  34. package/dist/cjs/utils/types.js.map +1 -0
  35. package/dist/cjs/utils/url.d.ts +9 -0
  36. package/dist/cjs/utils/url.js +32 -0
  37. package/dist/cjs/utils/url.js.map +1 -0
  38. package/package.json +68 -0
@@ -0,0 +1,813 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.playwrightLocatorPortadom = exports.playwrightHandlePortadom = exports.cheerioPortadom = exports.browserPortadom = void 0;
16
+ const get_1 = __importDefault(require("lodash/get"));
17
+ const format_1 = require("../utils/format");
18
+ const url_1 = require("../utils/url");
19
+ const error_1 = require("../utils/error");
20
+ const domUtils_1 = require("./domUtils");
21
+ const types_1 = require("./types");
22
+ /** Implementation of Portadom in browser (using Browser API) */
23
+ const browserPortadom = (node) => {
24
+ const getDoc = () => node.ownerDocument;
25
+ ///////////////////////
26
+ // SCALAR OPERATIONS
27
+ ///////////////////////
28
+ const text = ({ allowEmpty } = {}) => {
29
+ var _a, _b;
30
+ const txt = (_b = (_a = node.textContent) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : null;
31
+ return (0, format_1.strOrNull)(txt, allowEmpty);
32
+ };
33
+ const textAsUpper = (options) => {
34
+ const txt = text(options);
35
+ return txt ? txt.toLocaleUpperCase() : txt;
36
+ };
37
+ const textAsLower = (options) => {
38
+ const txt = text(options);
39
+ return txt ? txt.toLocaleLowerCase() : txt;
40
+ };
41
+ const textAsNumber = (options) => {
42
+ const txt = text(options);
43
+ return (0, format_1.strAsNumber)(txt, options);
44
+ };
45
+ const prop = (propOrPath, { allowEmpty = false } = {}) => {
46
+ var _a;
47
+ const propPath = Array.isArray(propOrPath) ? propOrPath : [propOrPath];
48
+ let propVal = (_a = (0, get_1.default)(node, propPath)) !== null && _a !== void 0 ? _a : null;
49
+ propVal = typeof propVal === 'string' ? propVal.trim() : propVal;
50
+ return (0, format_1.strOrNull)(propVal, allowEmpty);
51
+ };
52
+ const props = (propsOrPaths, options = {}) => {
53
+ const propPaths = propsOrPaths.map((p) => (Array.isArray(p) ? p : [p]));
54
+ const propData = propPaths.map((path) => prop(path, options));
55
+ return propData;
56
+ };
57
+ const attr = (propName, { allowEmpty } = {}) => {
58
+ var _a;
59
+ let attrVal = (_a = node.getAttribute(propName)) !== null && _a !== void 0 ? _a : null;
60
+ attrVal = typeof attrVal === 'string' ? attrVal.trim() : attrVal;
61
+ return (0, format_1.strOrNull)(attrVal, allowEmpty);
62
+ };
63
+ const attrs = (attrNames, options = {}) => {
64
+ const attrData = attrNames.reduce((agg, name) => {
65
+ agg[name] = attr(name, options);
66
+ return agg;
67
+ }, {});
68
+ return attrData;
69
+ };
70
+ const href = ({ allowEmpty, allowRelative, baseUrl } = {}) => __awaiter(void 0, void 0, void 0, function* () {
71
+ const val = prop('href', { allowEmpty });
72
+ return (0, url_1.formatUrl)(val, { allowRelative, baseUrl: baseUrl !== null && baseUrl !== void 0 ? baseUrl : (yield url()) });
73
+ });
74
+ const src = ({ allowEmpty, allowRelative, baseUrl } = {}) => __awaiter(void 0, void 0, void 0, function* () {
75
+ const val = prop('src', { allowEmpty });
76
+ return (0, url_1.formatUrl)(val, { allowRelative, baseUrl: baseUrl !== null && baseUrl !== void 0 ? baseUrl : (yield url()) });
77
+ });
78
+ const nodeName = () => {
79
+ // On UPPER- vs lower-case https://stackoverflow.com/questions/27223756/
80
+ const val = prop('nodeName');
81
+ return typeof val === 'string' ? val.toLocaleUpperCase() : val;
82
+ };
83
+ const url = () => {
84
+ var _a, _b;
85
+ // See https://stackoverflow.com/a/16010322/9788634
86
+ const urlVal = ((_b = (_a = getDoc().defaultView) === null || _a === void 0 ? void 0 : _a.location) === null || _b === void 0 ? void 0 : _b.href) || null;
87
+ return urlVal;
88
+ };
89
+ const map = (mapFn) => {
90
+ return mapFn(node);
91
+ };
92
+ ///////////////////////
93
+ // NODE OPERATIONS
94
+ ///////////////////////
95
+ const findOne = (selector) => {
96
+ var _a;
97
+ if (![Node.ELEMENT_NODE, Node.DOCUMENT_NODE].includes(node.nodeType)) {
98
+ return (0, types_1.createPortadomPromise)(null);
99
+ }
100
+ const resultEl = ((_a = node.querySelector(selector)) !== null && _a !== void 0 ? _a : null);
101
+ const dom = resultEl ? (0, exports.browserPortadom)(resultEl) : null;
102
+ return (0, types_1.createPortadomPromise)(dom);
103
+ };
104
+ const findMany = (selector) => {
105
+ if (![Node.ELEMENT_NODE, Node.DOCUMENT_NODE].includes(node.nodeType)) {
106
+ return (0, types_1.createPortadomArrayPromise)([]);
107
+ }
108
+ const resultEls = [...node.querySelectorAll(selector)]; // prettier-ignore
109
+ const dom = resultEls.map((el) => (0, exports.browserPortadom)(el));
110
+ return (0, types_1.createPortadomArrayPromise)(dom);
111
+ };
112
+ const closest = (selector) => {
113
+ var _a;
114
+ if (![Node.ELEMENT_NODE, Node.DOCUMENT_NODE].includes(node.nodeType)) {
115
+ return (0, types_1.createPortadomPromise)(null);
116
+ }
117
+ const resultEl = ((_a = node.closest(selector)) !== null && _a !== void 0 ? _a : null);
118
+ const dom = resultEl ? (0, exports.browserPortadom)(resultEl) : null;
119
+ return (0, types_1.createPortadomPromise)(dom);
120
+ };
121
+ const parent = () => {
122
+ const parentEl = (node.parentNode || null);
123
+ const dom = parentEl ? (0, exports.browserPortadom)(parentEl) : null;
124
+ return (0, types_1.createPortadomPromise)(dom);
125
+ };
126
+ const children = () => {
127
+ const childEls = [...node.children];
128
+ const dom = childEls.map((el) => (0, exports.browserPortadom)(el));
129
+ return (0, types_1.createPortadomArrayPromise)(dom);
130
+ };
131
+ const root = () => {
132
+ var _a;
133
+ const rootEl = (((_a = node.ownerDocument) === null || _a === void 0 ? void 0 : _a.documentElement) || null);
134
+ const dom = rootEl ? (0, exports.browserPortadom)(rootEl) : null;
135
+ return (0, types_1.createPortadomPromise)(dom);
136
+ };
137
+ const remove = () => {
138
+ node.remove();
139
+ };
140
+ const _getCommonAncestor = (el1, el2) => {
141
+ // https://stackoverflow.com/a/25154092
142
+ // https://developer.mozilla.org/en-US/docs/Web/API/Range/commonAncestorContainer
143
+ const _getCommonAncestorFromRange = (el1, el2) => {
144
+ const range = new Range();
145
+ range.setStartBefore(el1);
146
+ range.setEndAfter(el2);
147
+ const containerEl = range.commonAncestorContainer;
148
+ return containerEl;
149
+ };
150
+ // https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition
151
+ const isEl1BeforeEl2 = !!(el1.compareDocumentPosition(el2) & Node.DOCUMENT_POSITION_FOLLOWING);
152
+ const { firstEl, lastEl } = isEl1BeforeEl2
153
+ ? { firstEl: el1, lastEl: el2 }
154
+ : { firstEl: el2, lastEl: el1 };
155
+ const containerEl = _getCommonAncestorFromRange(firstEl, lastEl);
156
+ return containerEl;
157
+ };
158
+ const getCommonAncestor = (otherEl) => {
159
+ const ancestor = _getCommonAncestor(node, otherEl);
160
+ const dom = ancestor ? (0, exports.browserPortadom)(ancestor) : null;
161
+ return (0, types_1.createPortadomPromise)(dom);
162
+ };
163
+ const _getCommonAncestorFromSelector = _createCommonAncestorFromSelectorFn({
164
+ querySelectorAll: (selector) => node.querySelectorAll(selector),
165
+ getParent: (el) => el.parentElement,
166
+ isAncestor: (el1, el2) => {
167
+ return !!(el1.compareDocumentPosition(el2) & Node.DOCUMENT_POSITION_CONTAINED_BY);
168
+ },
169
+ getCommonAncestor: (el1, el2) => _getCommonAncestor(el1, el2),
170
+ });
171
+ const getCommonAncestorFromSelector = (selector) => {
172
+ const dom = _getCommonAncestorFromSelector(selector).then((ancestor) => (ancestor ? (0, exports.browserPortadom)(ancestor) : null));
173
+ return (0, types_1.createPortadomPromise)(dom);
174
+ };
175
+ return {
176
+ node,
177
+ text,
178
+ textAsLower,
179
+ textAsUpper,
180
+ textAsNumber,
181
+ attr,
182
+ attrs,
183
+ prop,
184
+ props,
185
+ href,
186
+ src,
187
+ nodeName,
188
+ url,
189
+ map,
190
+ findOne,
191
+ findMany,
192
+ closest,
193
+ parent,
194
+ children,
195
+ root,
196
+ remove,
197
+ getCommonAncestor,
198
+ getCommonAncestorFromSelector,
199
+ };
200
+ };
201
+ exports.browserPortadom = browserPortadom;
202
+ /** Implementation of Portadom in Cheerio */
203
+ const cheerioPortadom = (cheerioNode, srcUrl) => {
204
+ ///////////////////////
205
+ // SCALAR OPERATIONS
206
+ ///////////////////////
207
+ const text = ({ allowEmpty } = {}) => {
208
+ var _a, _b;
209
+ const txt = (_b = (_a = cheerioNode.text()) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : null;
210
+ return (0, format_1.strOrNull)(txt, allowEmpty);
211
+ };
212
+ const textAsUpper = (options) => {
213
+ const txt = text(options);
214
+ return txt ? txt.toLocaleUpperCase() : txt;
215
+ };
216
+ const textAsLower = (options) => {
217
+ const txt = text(options);
218
+ return txt ? txt.toLocaleLowerCase() : txt;
219
+ };
220
+ const textAsNumber = (options) => {
221
+ const txt = text(options);
222
+ return (0, format_1.strAsNumber)(txt, options);
223
+ };
224
+ const attr = (attrName, { allowEmpty } = {}) => {
225
+ var _a;
226
+ let attrVal = (_a = cheerioNode.attr(attrName)) !== null && _a !== void 0 ? _a : null;
227
+ attrVal = typeof attrVal === 'string' ? attrVal.trim() : attrVal;
228
+ return (0, format_1.strOrNull)(attrVal, allowEmpty);
229
+ };
230
+ const attrs = (attrNames, options = {}) => {
231
+ const attrData = attrNames.reduce((agg, name) => {
232
+ agg[name] = attr(name, options);
233
+ return agg;
234
+ }, {});
235
+ return attrData;
236
+ };
237
+ const prop = (propOrPath, { allowEmpty = false } = {}) => {
238
+ var _a;
239
+ const propPath = Array.isArray(propOrPath) ? propOrPath : [propOrPath];
240
+ let propVal = (_a = cheerioNode.prop(propPath[0])) !== null && _a !== void 0 ? _a : null;
241
+ if (propPath.length > 1)
242
+ propVal = (0, get_1.default)(propVal, propPath.slice(1));
243
+ propVal = typeof propVal === 'string' ? propVal.trim() : propVal;
244
+ return (0, format_1.strOrNull)(propVal, allowEmpty);
245
+ };
246
+ const props = (propsOrPaths, options = {}) => {
247
+ const propPaths = propsOrPaths.map((p) => (Array.isArray(p) ? p : [p]));
248
+ const propData = propPaths.map((path) => prop(path, options));
249
+ return propData;
250
+ };
251
+ const href = ({ allowEmpty, allowRelative, baseUrl } = {}) => {
252
+ const val = prop('href', { allowEmpty });
253
+ return (0, url_1.formatUrl)(val, { allowRelative, baseUrl: baseUrl !== null && baseUrl !== void 0 ? baseUrl : srcUrl });
254
+ };
255
+ const src = ({ allowEmpty, allowRelative, baseUrl } = {}) => {
256
+ const val = prop('src', { allowEmpty });
257
+ return (0, url_1.formatUrl)(val, { allowRelative, baseUrl: baseUrl !== null && baseUrl !== void 0 ? baseUrl : srcUrl });
258
+ };
259
+ const nodeName = () => {
260
+ // On UPPER- vs lower-case https://stackoverflow.com/questions/27223756/
261
+ const val = prop('nodeName');
262
+ return typeof val === 'string' ? val.toLocaleUpperCase() : val;
263
+ };
264
+ const url = () => {
265
+ return srcUrl !== null && srcUrl !== void 0 ? srcUrl : null;
266
+ };
267
+ const map = (mapFn) => {
268
+ return mapFn(cheerioNode);
269
+ };
270
+ ///////////////////////
271
+ // NODE OPERATIONS
272
+ ///////////////////////
273
+ const findOne = (selector) => {
274
+ const resultEl = cheerioNode.find(selector).first();
275
+ if (!resultEl.get(0))
276
+ return (0, types_1.createPortadomPromise)(null);
277
+ const dom = (0, exports.cheerioPortadom)(resultEl, srcUrl);
278
+ return (0, types_1.createPortadomPromise)(dom);
279
+ };
280
+ const findMany = (selector) => {
281
+ const resultEls = (0, domUtils_1.splitCheerioSelection)(cheerioNode.find(selector));
282
+ const doms = resultEls.map((ch) => (0, exports.cheerioPortadom)(ch, srcUrl));
283
+ return (0, types_1.createPortadomArrayPromise)(doms);
284
+ };
285
+ const closest = (selector) => {
286
+ const resultEl = cheerioNode.closest(selector).first();
287
+ if (!resultEl.get(0))
288
+ return (0, types_1.createPortadomPromise)(null);
289
+ const dom = (0, exports.cheerioPortadom)(resultEl, srcUrl);
290
+ return (0, types_1.createPortadomPromise)(dom);
291
+ };
292
+ const parent = () => {
293
+ const parentEl = cheerioNode.parent().first();
294
+ if (!parentEl.get(0))
295
+ return (0, types_1.createPortadomPromise)(null);
296
+ const dom = (0, exports.cheerioPortadom)(parentEl, srcUrl);
297
+ return (0, types_1.createPortadomPromise)(dom);
298
+ };
299
+ const children = () => {
300
+ const childEls = (0, domUtils_1.splitCheerioSelection)(cheerioNode.children());
301
+ const doms = childEls.map((ch) => (0, exports.cheerioPortadom)(ch, srcUrl));
302
+ return (0, types_1.createPortadomArrayPromise)(doms);
303
+ };
304
+ const root = () => {
305
+ var _a;
306
+ const rootEl = (_a = cheerioNode._root) === null || _a === void 0 ? void 0 : _a.first();
307
+ if (!(rootEl === null || rootEl === void 0 ? void 0 : rootEl.get(0)))
308
+ return (0, types_1.createPortadomPromise)(null);
309
+ const dom = (0, exports.cheerioPortadom)(rootEl, srcUrl);
310
+ return (0, types_1.createPortadomPromise)(dom);
311
+ };
312
+ const remove = () => {
313
+ cheerioNode.remove();
314
+ };
315
+ /** Function that finds the closest common ancestor for `el1` and `el2`. */
316
+ const _getCommonAncestor = (el1, el2) => __awaiter(void 0, void 0, void 0, function* () {
317
+ const ch1Parents = (0, domUtils_1.splitCheerioSelection)(el1.parents());
318
+ const ch2Parents = (0, domUtils_1.splitCheerioSelection)(el2.parents());
319
+ let commonAncestor = null;
320
+ for (const comparerParent of ch1Parents) {
321
+ for (const compareeParent of ch2Parents) {
322
+ if (!comparerParent.is(compareeParent))
323
+ continue;
324
+ commonAncestor = comparerParent;
325
+ break;
326
+ }
327
+ if (commonAncestor)
328
+ break;
329
+ }
330
+ return commonAncestor;
331
+ });
332
+ const getCommonAncestor = (otherEl) => {
333
+ const dom = _getCommonAncestor(cheerioNode, otherEl).then((ancestor) => {
334
+ if (!(ancestor === null || ancestor === void 0 ? void 0 : ancestor.get(0)))
335
+ return null;
336
+ return (0, exports.cheerioPortadom)(ancestor, srcUrl);
337
+ });
338
+ return (0, types_1.createPortadomPromise)(dom);
339
+ };
340
+ const _getCommonAncestorFromSelector = _createCommonAncestorFromSelectorFn({
341
+ querySelectorAll: (selector) => (0, domUtils_1.splitCheerioSelection)(cheerioNode.find(selector)),
342
+ getParent: (el) => el.parent(),
343
+ isAncestor: (el1, el2) => el1.is(el2.parents()),
344
+ getCommonAncestor: (el1, el2) => _getCommonAncestor(el1, el2),
345
+ });
346
+ const getCommonAncestorFromSelector = (selector) => {
347
+ const dom = _getCommonAncestorFromSelector(selector).then((ancestor) => {
348
+ if (!(ancestor === null || ancestor === void 0 ? void 0 : ancestor.get(0)))
349
+ return null;
350
+ return (0, exports.cheerioPortadom)(ancestor, srcUrl);
351
+ });
352
+ return (0, types_1.createPortadomPromise)(dom);
353
+ };
354
+ return {
355
+ node: cheerioNode,
356
+ text,
357
+ textAsLower,
358
+ textAsUpper,
359
+ textAsNumber,
360
+ attr,
361
+ attrs,
362
+ prop,
363
+ props,
364
+ href,
365
+ src,
366
+ nodeName,
367
+ url,
368
+ map,
369
+ findOne,
370
+ findMany,
371
+ closest,
372
+ parent,
373
+ children,
374
+ root,
375
+ remove,
376
+ getCommonAncestor,
377
+ getCommonAncestorFromSelector,
378
+ };
379
+ };
380
+ exports.cheerioPortadom = cheerioPortadom;
381
+ const verifyHandleEl = (el) => __awaiter(void 0, void 0, void 0, function* () {
382
+ if (!el)
383
+ return null;
384
+ const exists = yield el.evaluate((el) => !!el).catch(error_1.logAndRethrow);
385
+ return exists ? el : null;
386
+ });
387
+ /** Implementation of Portadom in Playwright using Handles */
388
+ const playwrightHandlePortadom = (node, page) => {
389
+ ///////////////////////
390
+ // SCALAR OPERATIONS
391
+ ///////////////////////
392
+ const text = ({ allowEmpty } = {}) => __awaiter(void 0, void 0, void 0, function* () {
393
+ var _a, _b;
394
+ const txt = (_b = (_a = (yield node.textContent())) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : null;
395
+ return (0, format_1.strOrNull)(txt, allowEmpty);
396
+ });
397
+ const textAsUpper = (options) => __awaiter(void 0, void 0, void 0, function* () {
398
+ const txt = yield text(options);
399
+ return txt ? txt.toLocaleUpperCase() : txt;
400
+ });
401
+ const textAsLower = (options) => __awaiter(void 0, void 0, void 0, function* () {
402
+ const txt = yield text(options);
403
+ return txt ? txt.toLocaleLowerCase() : txt;
404
+ });
405
+ const textAsNumber = (options) => __awaiter(void 0, void 0, void 0, function* () {
406
+ const txt = yield text(options);
407
+ return (0, format_1.strAsNumber)(txt, options);
408
+ });
409
+ const prop = (propOrPath, options = {}) => __awaiter(void 0, void 0, void 0, function* () {
410
+ const [resProp] = yield props([propOrPath], options);
411
+ return resProp;
412
+ });
413
+ const props = (propsOrPaths, { allowEmpty = false } = {}) => __awaiter(void 0, void 0, void 0, function* () {
414
+ const propPaths = propsOrPaths.map((p) => (Array.isArray(p) ? p : [p]));
415
+ const data = yield node
416
+ .evaluate((el, { propPaths, allowEmpty }) => {
417
+ return propPaths.map((propPath) => {
418
+ let val = el;
419
+ for (const prop of propPath) {
420
+ if (el == null)
421
+ break;
422
+ val = val[prop];
423
+ }
424
+ val = typeof val === 'string' ? val.trim() : val;
425
+ return (0, format_1.strOrNull)(val, allowEmpty);
426
+ });
427
+ }, { propPaths, allowEmpty })
428
+ .catch(error_1.logAndRethrow);
429
+ return data;
430
+ });
431
+ const attr = (attrName, options) => __awaiter(void 0, void 0, void 0, function* () {
432
+ var _c;
433
+ const data = yield attrs([attrName], options);
434
+ return (_c = data[attrName]) !== null && _c !== void 0 ? _c : null;
435
+ });
436
+ const attrs = (attrNames, { allowEmpty = false } = {}) => {
437
+ const data = node
438
+ .evaluate((el, { attrNames, allowEmpty }) => {
439
+ const attrData = attrNames.reduce((agg, name) => {
440
+ var _a;
441
+ let attrVal = (_a = el.getAttribute(name)) !== null && _a !== void 0 ? _a : null;
442
+ attrVal = typeof attrVal === 'string' ? attrVal.trim() : attrVal;
443
+ agg[name] = (0, format_1.strOrNull)(attrVal, allowEmpty);
444
+ return agg;
445
+ }, {});
446
+ return attrData;
447
+ }, { attrNames, allowEmpty })
448
+ .catch(error_1.logAndRethrow);
449
+ return data;
450
+ };
451
+ const href = ({ allowEmpty, allowRelative, baseUrl, } = {}) => __awaiter(void 0, void 0, void 0, function* () {
452
+ const val = (yield prop('href', { allowEmpty }));
453
+ return (0, url_1.formatUrl)(val, { allowRelative, baseUrl: baseUrl !== null && baseUrl !== void 0 ? baseUrl : (yield url()) });
454
+ });
455
+ const src = ({ allowEmpty, allowRelative, baseUrl, } = {}) => __awaiter(void 0, void 0, void 0, function* () {
456
+ const val = (yield prop('src', { allowEmpty }));
457
+ return (0, url_1.formatUrl)(val, { allowRelative, baseUrl: baseUrl !== null && baseUrl !== void 0 ? baseUrl : (yield url()) });
458
+ });
459
+ const nodeName = () => __awaiter(void 0, void 0, void 0, function* () {
460
+ // On UPPER- vs lower-case https://stackoverflow.com/questions/27223756/
461
+ const val = (yield prop('nodeName'));
462
+ return typeof val === 'string' ? val.toLocaleUpperCase() : val;
463
+ });
464
+ const url = () => __awaiter(void 0, void 0, void 0, function* () {
465
+ return page.url() || null;
466
+ });
467
+ const map = (mapFn) => {
468
+ return mapFn(node);
469
+ };
470
+ ///////////////////////
471
+ // NODE OPERATIONS
472
+ ///////////////////////
473
+ const findOne = (selector) => {
474
+ const dom = node
475
+ .evaluateHandle((el, s) => {
476
+ if (![Node.ELEMENT_NODE, Node.DOCUMENT_NODE].includes(el.nodeType))
477
+ return null;
478
+ return el.querySelector(s) || null;
479
+ }, selector)
480
+ .catch(error_1.logAndRethrow)
481
+ .then((resultEl) => __awaiter(void 0, void 0, void 0, function* () {
482
+ const hasResult = yield verifyHandleEl(resultEl);
483
+ return hasResult ? (0, exports.playwrightHandlePortadom)(resultEl, page) : null;
484
+ }));
485
+ return (0, types_1.createPortadomPromise)(dom);
486
+ };
487
+ const findMany = (selector) => {
488
+ const doms = node
489
+ .evaluateHandle((el, s) => {
490
+ if (![Node.ELEMENT_NODE, Node.DOCUMENT_NODE].includes(el.nodeType))
491
+ return [];
492
+ return [...el.querySelectorAll(s)];
493
+ }, selector)
494
+ .catch(error_1.logAndRethrow)
495
+ .then((elsHandle) => __awaiter(void 0, void 0, void 0, function* () {
496
+ const resultEls = yield (0, domUtils_1.splitPlaywrightSelection)(elsHandle);
497
+ return resultEls.map((el) => (0, exports.playwrightHandlePortadom)(el, page));
498
+ }));
499
+ return (0, types_1.createPortadomArrayPromise)(doms);
500
+ };
501
+ const closest = (selector) => {
502
+ const dom = node
503
+ .evaluateHandle((el, s) => {
504
+ if (![Node.ELEMENT_NODE, Node.DOCUMENT_NODE].includes(el.nodeType))
505
+ return null;
506
+ return el.closest(s) || null;
507
+ }, selector)
508
+ .catch(error_1.logAndRethrow)
509
+ .then((resultEl) => __awaiter(void 0, void 0, void 0, function* () {
510
+ const hasResult = yield verifyHandleEl(resultEl);
511
+ return hasResult ? (0, exports.playwrightHandlePortadom)(resultEl, page) : null;
512
+ }));
513
+ return (0, types_1.createPortadomPromise)(dom);
514
+ };
515
+ const parent = () => {
516
+ const dom = node
517
+ .evaluateHandle((el) => el.parentElement || null)
518
+ .catch(error_1.logAndRethrow)
519
+ .then((resultEl) => __awaiter(void 0, void 0, void 0, function* () {
520
+ const hasResult = yield verifyHandleEl(resultEl);
521
+ return hasResult ? (0, exports.playwrightHandlePortadom)(resultEl, page) : null;
522
+ }));
523
+ return (0, types_1.createPortadomPromise)(dom);
524
+ };
525
+ const children = () => {
526
+ const doms = node
527
+ .evaluateHandle((el) => [...el.children])
528
+ .catch(error_1.logAndRethrow)
529
+ .then((elsHandle) => __awaiter(void 0, void 0, void 0, function* () {
530
+ const resultEls = yield (0, domUtils_1.splitPlaywrightSelection)(elsHandle);
531
+ return resultEls.map((el) => (0, exports.playwrightHandlePortadom)(el, page));
532
+ }));
533
+ return (0, types_1.createPortadomArrayPromise)(doms);
534
+ };
535
+ const root = () => {
536
+ const dom = node
537
+ .evaluateHandle((el) => { var _a; return ((_a = el.ownerDocument) === null || _a === void 0 ? void 0 : _a.documentElement) || null; })
538
+ .catch(error_1.logAndRethrow)
539
+ .then((resultEl) => __awaiter(void 0, void 0, void 0, function* () {
540
+ const hasResult = yield verifyHandleEl(resultEl);
541
+ return hasResult ? (0, exports.playwrightHandlePortadom)(resultEl, page) : null;
542
+ }));
543
+ return (0, types_1.createPortadomPromise)(dom);
544
+ };
545
+ const remove = () => __awaiter(void 0, void 0, void 0, function* () {
546
+ yield node.evaluate((el) => el.remove()).catch(error_1.logAndRethrow);
547
+ });
548
+ const _getCommonAncestor = (loc1, loc2) => __awaiter(void 0, void 0, void 0, function* () {
549
+ const isEl1BeforeEl2 = yield (yield (0, domUtils_1.mergeHandles)([loc1, loc2]))
550
+ .evaluate(([el1, el2]) => {
551
+ if (!el1 || !el2)
552
+ return false;
553
+ // https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition
554
+ const result = !!(el1.compareDocumentPosition(el2) & Node.DOCUMENT_POSITION_FOLLOWING);
555
+ return result;
556
+ })
557
+ .catch(error_1.logAndRethrow);
558
+ const { firstEl, lastEl } = isEl1BeforeEl2
559
+ ? { firstEl: loc1, lastEl: loc2 }
560
+ : { firstEl: loc2, lastEl: loc1 };
561
+ const ancestor = yield (yield (0, domUtils_1.mergeHandles)([firstEl, lastEl]))
562
+ .evaluateHandle(([el1, el2]) => {
563
+ if (!el1 || !el2)
564
+ return null;
565
+ // https://stackoverflow.com/a/25154092
566
+ // https://developer.mozilla.org/en-US/docs/Web/API/Range/commonAncestorContainer
567
+ const range = new Range();
568
+ range.setStartBefore(el1);
569
+ range.setEndAfter(el2);
570
+ const containerEl = range.commonAncestorContainer;
571
+ return containerEl;
572
+ })
573
+ .catch(error_1.logAndRethrow);
574
+ const hasResult = yield ancestor.evaluate((el) => !!el).catch(error_1.logAndRethrow);
575
+ return hasResult ? ancestor : null;
576
+ });
577
+ const getCommonAncestor = (otherEl) => {
578
+ const dom = _getCommonAncestor(node, otherEl).then((resultEl) => __awaiter(void 0, void 0, void 0, function* () {
579
+ const hasResult = yield verifyHandleEl(resultEl);
580
+ return resultEl && hasResult
581
+ ? (0, exports.playwrightHandlePortadom)(resultEl, page)
582
+ : null;
583
+ }));
584
+ return (0, types_1.createPortadomPromise)(dom);
585
+ };
586
+ const _getCommonAncestorFromSelector = _createCommonAncestorFromSelectorFn({
587
+ querySelectorAll: (selector) => __awaiter(void 0, void 0, void 0, function* () {
588
+ const resultEls = (yield findMany(selector).map((d) => d.node)).filter(Boolean);
589
+ return resultEls;
590
+ }),
591
+ getParent: (el) => __awaiter(void 0, void 0, void 0, function* () {
592
+ return (0, exports.playwrightHandlePortadom)(el, page).parent().node;
593
+ }),
594
+ isAncestor: (el1, el2) => __awaiter(void 0, void 0, void 0, function* () {
595
+ return (yield (0, domUtils_1.mergeHandles)([el1, el2]))
596
+ .evaluate(([el1, el2]) => {
597
+ if (!el1 || !el2)
598
+ return false;
599
+ // https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition
600
+ const result = !!(el1.compareDocumentPosition(el2) & Node.DOCUMENT_POSITION_CONTAINED_BY);
601
+ return result;
602
+ })
603
+ .catch(error_1.logAndRethrow);
604
+ }),
605
+ getCommonAncestor: _getCommonAncestor,
606
+ });
607
+ const getCommonAncestorFromSelector = (selector) => {
608
+ const dom = _getCommonAncestorFromSelector(selector).then((resultEl) => __awaiter(void 0, void 0, void 0, function* () {
609
+ const hasResult = yield verifyHandleEl(resultEl);
610
+ return resultEl && hasResult
611
+ ? (0, exports.playwrightHandlePortadom)(resultEl, page)
612
+ : null;
613
+ }));
614
+ return (0, types_1.createPortadomPromise)(dom);
615
+ };
616
+ return {
617
+ node,
618
+ text,
619
+ textAsLower,
620
+ textAsUpper,
621
+ textAsNumber,
622
+ attr,
623
+ attrs,
624
+ prop,
625
+ props,
626
+ href,
627
+ src,
628
+ nodeName,
629
+ url,
630
+ map,
631
+ findOne,
632
+ findMany,
633
+ closest,
634
+ parent,
635
+ children,
636
+ root,
637
+ remove,
638
+ getCommonAncestor,
639
+ getCommonAncestorFromSelector,
640
+ };
641
+ };
642
+ exports.playwrightHandlePortadom = playwrightHandlePortadom;
643
+ /** Implementation of Portadom in Playwright using Locators */
644
+ const playwrightLocatorPortadom = (node, page) => {
645
+ // NOTE: Where possible, make use of the already-existing logic for Handles
646
+ const handleDom = (0, exports.playwrightHandlePortadom)(node, page);
647
+ ///////////////////////
648
+ // SCALAR OPERATIONS
649
+ ///////////////////////
650
+ /** Empty */
651
+ ///////////////////////
652
+ // NODE OPERATIONS
653
+ ///////////////////////
654
+ const findOne = (selector) => {
655
+ const resultEl = node.locator(selector);
656
+ const dom = verifyHandleEl(resultEl).then((hasResult) => {
657
+ return hasResult ? (0, exports.playwrightLocatorPortadom)(resultEl, page) : null;
658
+ });
659
+ return (0, types_1.createPortadomPromise)(dom);
660
+ };
661
+ const findMany = (selector) => {
662
+ const doms = node
663
+ .locator(selector)
664
+ .all()
665
+ .then((resultEls) => {
666
+ return resultEls.map((el) => (0, exports.playwrightLocatorPortadom)(el, page));
667
+ });
668
+ return (0, types_1.createPortadomArrayPromise)(doms);
669
+ };
670
+ const closest = (selector) => {
671
+ // Find closest ancestor matching the given selector.
672
+ // See https://stackoverflow.com/a/40333166/9788634
673
+ const resultEl = node.locator(`xpath=ancestor::${selector.trim()}[position() = 1]`);
674
+ const dom = verifyHandleEl(resultEl).then((hasResult) => {
675
+ return hasResult ? (0, exports.playwrightLocatorPortadom)(resultEl, page) : null;
676
+ });
677
+ return (0, types_1.createPortadomPromise)(dom);
678
+ };
679
+ const parent = () => {
680
+ return closest('*');
681
+ };
682
+ const children = () => {
683
+ const doms = node
684
+ .locator('xpath=child::*')
685
+ .all()
686
+ .then((resultEls) => {
687
+ return resultEls.map((el) => (0, exports.playwrightLocatorPortadom)(el, page));
688
+ });
689
+ return (0, types_1.createPortadomArrayPromise)(doms);
690
+ };
691
+ const root = () => {
692
+ // Find closest ancestor matching the given selector.
693
+ // See https://stackoverflow.com/a/40333166/9788634 and https://stackoverflow.com/q/31562639/9788634
694
+ const resultEl = node.locator(`xpath=ancestor::*[position() = last()]`);
695
+ const dom = verifyHandleEl(resultEl).then((hasResult) => {
696
+ return hasResult ? (0, exports.playwrightLocatorPortadom)(resultEl, page) : null;
697
+ });
698
+ return (0, types_1.createPortadomPromise)(dom);
699
+ };
700
+ const remove = () => __awaiter(void 0, void 0, void 0, function* () {
701
+ yield node.evaluate((el) => el.remove()).catch(error_1.logAndRethrow);
702
+ });
703
+ /** Function that finds the closest common ancestor for `el1` and `el2`. */
704
+ const _getCommonAncestor = (el1, el2) => __awaiter(void 0, void 0, void 0, function* () {
705
+ const ch1Parents = yield el1.locator(`xpath=ancestor::*`).all();
706
+ const ch2Parents = yield el2.locator(`xpath=ancestor::*`).all();
707
+ let commonAncestor = null;
708
+ for (const comparerParent of ch1Parents) {
709
+ for (const compareeParent of ch2Parents) {
710
+ const handle = yield (0, domUtils_1.mergeHandles)([comparerParent, compareeParent]);
711
+ const isSame = yield handle.evaluate(([el1, el2]) => {
712
+ return el1 === el2;
713
+ });
714
+ if (isSame) {
715
+ commonAncestor = comparerParent;
716
+ break;
717
+ }
718
+ }
719
+ if (commonAncestor)
720
+ break;
721
+ }
722
+ return commonAncestor;
723
+ });
724
+ const getCommonAncestor = (otherEl) => {
725
+ const dom = _getCommonAncestor(node, otherEl).then((resultEl) => __awaiter(void 0, void 0, void 0, function* () {
726
+ const hasResult = yield verifyHandleEl(resultEl);
727
+ return resultEl && hasResult
728
+ ? (0, exports.playwrightLocatorPortadom)(resultEl, page)
729
+ : null;
730
+ }));
731
+ return (0, types_1.createPortadomPromise)(dom);
732
+ };
733
+ const _getCommonAncestorFromSelector = _createCommonAncestorFromSelectorFn({
734
+ querySelectorAll: (selector) => __awaiter(void 0, void 0, void 0, function* () {
735
+ const resultEls = (yield findMany(selector).map((d) => d.node)).filter(Boolean);
736
+ return resultEls;
737
+ }),
738
+ getParent: (el) => __awaiter(void 0, void 0, void 0, function* () {
739
+ return (0, exports.playwrightLocatorPortadom)(el, page).parent().node;
740
+ }),
741
+ isAncestor: (el1, el2) => __awaiter(void 0, void 0, void 0, function* () {
742
+ return (yield (0, domUtils_1.mergeHandles)([el1, el2]))
743
+ .evaluate(([el1, el2]) => {
744
+ if (!el1 || !el2)
745
+ return false;
746
+ // https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition
747
+ const result = !!(el1.compareDocumentPosition(el2) & Node.DOCUMENT_POSITION_CONTAINED_BY);
748
+ return result;
749
+ })
750
+ .catch(error_1.logAndRethrow);
751
+ }),
752
+ getCommonAncestor: _getCommonAncestor,
753
+ });
754
+ const getCommonAncestorFromSelector = (selector) => {
755
+ const dom = _getCommonAncestorFromSelector(selector).then((ancestor) => __awaiter(void 0, void 0, void 0, function* () {
756
+ const hasResult = yield (ancestor === null || ancestor === void 0 ? void 0 : ancestor.evaluate((el) => !!el).catch(error_1.logAndRethrow));
757
+ return ancestor && hasResult
758
+ ? (0, exports.playwrightHandlePortadom)(ancestor, page)
759
+ : null;
760
+ }));
761
+ return (0, types_1.createPortadomPromise)(dom);
762
+ };
763
+ return {
764
+ node,
765
+ text: handleDom.text,
766
+ textAsLower: handleDom.textAsLower,
767
+ textAsUpper: handleDom.textAsUpper,
768
+ textAsNumber: handleDom.textAsNumber,
769
+ attr: handleDom.attr,
770
+ attrs: handleDom.attrs,
771
+ prop: handleDom.prop,
772
+ props: handleDom.props,
773
+ href: handleDom.href,
774
+ src: handleDom.src,
775
+ nodeName: handleDom.nodeName,
776
+ url: handleDom.url,
777
+ map: handleDom.map,
778
+ findOne,
779
+ findMany,
780
+ closest,
781
+ parent,
782
+ children,
783
+ root,
784
+ remove,
785
+ getCommonAncestor,
786
+ getCommonAncestorFromSelector,
787
+ };
788
+ };
789
+ exports.playwrightLocatorPortadom = playwrightLocatorPortadom;
790
+ const _createCommonAncestorFromSelectorFn = (input) => {
791
+ const getCommonAncestorFromSelector = (selector) => __awaiter(void 0, void 0, void 0, function* () {
792
+ const els = [...(yield input.querySelectorAll(selector))];
793
+ if (!els.length)
794
+ return null;
795
+ if (els.length === 1)
796
+ return input.getParent(els[0]);
797
+ const comparerEl = els.shift();
798
+ let ancestorEl = null;
799
+ for (const el of els) {
800
+ const currAncestorEl = comparerEl ? yield input.getCommonAncestor(comparerEl, el) : null;
801
+ const newAncestorEl = !ancestorEl
802
+ ? currAncestorEl
803
+ : currAncestorEl && (yield input.isAncestor(currAncestorEl, ancestorEl))
804
+ ? currAncestorEl
805
+ : ancestorEl;
806
+ ancestorEl = newAncestorEl;
807
+ }
808
+ return ancestorEl;
809
+ });
810
+ return getCommonAncestorFromSelector;
811
+ };
812
+ (0, exports.browserPortadom)(document.body);
813
+ //# sourceMappingURL=dom.js.map