puppeteer-element2selector
Advanced tools
Comparing version 0.0.1 to 0.0.2
@@ -5,7 +5,7 @@ # Changelog | ||
### 0.0.1 (2020-02-24) | ||
### [0.0.2](https://github.com/wadackel/puppeteer-element2selector/compare/v0.0.1...v0.0.2) (2020-11-23) | ||
### Features | ||
### Bug Fixes | ||
- Initial implement ([064fa82](https://github.com/wadackel/puppeteer-element2selector/commit/064fa82991d6f9cf83864373abd962c6e8678de5)) | ||
- Improve error handling ([e29ef2b](https://github.com/wadackel/puppeteer-element2selector/commit/e29ef2b30b466142aedafa93b094ffbc08a5bd0e)) | ||
@@ -17,7 +17,1 @@ ### 0.0.1 (2020-02-24) | ||
- Initial implement ([064fa82](https://github.com/wadackel/puppeteer-element2selector/commit/064fa82991d6f9cf83864373abd962c6e8678de5)) | ||
### 0.0.1 (2020-02-24) | ||
### Features | ||
- Initial implement ([064fa82](https://github.com/wadackel/puppeteer-element2selector/commit/064fa82991d6f9cf83864373abd962c6e8678de5)) |
var finder = (function () { | ||
'use strict'; | ||
'use strict'; | ||
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; | ||
var Limit; | ||
(function (Limit) { | ||
Limit[Limit["All"] = 0] = "All"; | ||
Limit[Limit["Two"] = 1] = "Two"; | ||
Limit[Limit["One"] = 2] = "One"; | ||
})(Limit || (Limit = {})); | ||
let config; | ||
let rootDocument; | ||
function finder(input, options) { | ||
if (input.nodeType !== Node.ELEMENT_NODE) { | ||
throw new Error(`Can't generate CSS selector for non-element node type.`); | ||
} | ||
if ("html" === input.tagName.toLowerCase()) { | ||
return "html"; | ||
} | ||
const defaults = { | ||
root: document.body, | ||
idName: (name) => true, | ||
className: (name) => true, | ||
tagName: (name) => true, | ||
attr: (name, value) => false, | ||
seedMinLength: 1, | ||
optimizedMinLength: 2, | ||
threshold: 1000, | ||
maxNumberOfTries: 10000, | ||
}; | ||
config = Object.assign(Object.assign({}, defaults), options); | ||
rootDocument = findRootDocument(config.root, defaults); | ||
let path = bottomUpSearch(input, Limit.All, () => bottomUpSearch(input, Limit.Two, () => bottomUpSearch(input, Limit.One))); | ||
if (path) { | ||
const optimized = sort(optimize(path, input)); | ||
if (optimized.length > 0) { | ||
path = optimized[0]; | ||
} | ||
return selector(path); | ||
} | ||
else { | ||
throw new Error(`Selector was not found.`); | ||
} | ||
} | ||
function findRootDocument(rootNode, defaults) { | ||
if (rootNode.nodeType === Node.DOCUMENT_NODE) { | ||
return rootNode; | ||
} | ||
if (rootNode === defaults.root) { | ||
return rootNode.ownerDocument; | ||
} | ||
return rootNode; | ||
} | ||
function bottomUpSearch(input, limit, fallback) { | ||
let path = null; | ||
let stack = []; | ||
let current = input; | ||
let i = 0; | ||
while (current && current !== config.root.parentElement) { | ||
let level = maybe(id(current)) || maybe(...attr(current)) || maybe(...classNames(current)) || maybe(tagName(current)) || [any()]; | ||
const nth = index(current); | ||
if (limit === Limit.All) { | ||
if (nth) { | ||
level = level.concat(level.filter(dispensableNth).map(node => nthChild(node, nth))); | ||
} | ||
} | ||
else if (limit === Limit.Two) { | ||
level = level.slice(0, 1); | ||
if (nth) { | ||
level = level.concat(level.filter(dispensableNth).map(node => nthChild(node, nth))); | ||
} | ||
} | ||
else if (limit === Limit.One) { | ||
const [node] = level = level.slice(0, 1); | ||
if (nth && dispensableNth(node)) { | ||
level = [nthChild(node, nth)]; | ||
} | ||
} | ||
for (let node of level) { | ||
node.level = i; | ||
} | ||
stack.push(level); | ||
if (stack.length >= config.seedMinLength) { | ||
path = findUniquePath(stack, fallback); | ||
if (path) { | ||
break; | ||
} | ||
} | ||
current = current.parentElement; | ||
i++; | ||
} | ||
if (!path) { | ||
path = findUniquePath(stack, fallback); | ||
} | ||
return path; | ||
} | ||
function findUniquePath(stack, fallback) { | ||
const paths = sort(combinations(stack)); | ||
if (paths.length > config.threshold) { | ||
return fallback ? fallback() : null; | ||
} | ||
for (let candidate of paths) { | ||
if (unique(candidate)) { | ||
return candidate; | ||
} | ||
} | ||
return null; | ||
} | ||
function selector(path) { | ||
let node = path[0]; | ||
let query = node.name; | ||
for (let i = 1; i < path.length; i++) { | ||
const level = path[i].level || 0; | ||
if (node.level === level - 1) { | ||
query = `${path[i].name} > ${query}`; | ||
} | ||
else { | ||
query = `${path[i].name} ${query}`; | ||
} | ||
node = path[i]; | ||
} | ||
return query; | ||
} | ||
function penalty(path) { | ||
return path.map(node => node.penalty).reduce((acc, i) => acc + i, 0); | ||
} | ||
function unique(path) { | ||
switch (rootDocument.querySelectorAll(selector(path)).length) { | ||
case 0: | ||
throw new Error(`Can't select any node with this selector: ${selector(path)}`); | ||
case 1: | ||
return true; | ||
default: | ||
return false; | ||
} | ||
} | ||
function id(input) { | ||
const elementId = input.getAttribute("id"); | ||
if (elementId && config.idName(elementId)) { | ||
return { | ||
name: "#" + cssesc(elementId, { isIdentifier: true }), | ||
penalty: 0, | ||
}; | ||
} | ||
return null; | ||
} | ||
function attr(input) { | ||
const attrs = Array.from(input.attributes).filter((attr) => config.attr(attr.name, attr.value)); | ||
return attrs.map((attr) => ({ | ||
name: "[" + cssesc(attr.name, { isIdentifier: true }) + "=\"" + cssesc(attr.value) + "\"]", | ||
penalty: 0.5 | ||
})); | ||
} | ||
function classNames(input) { | ||
const names = Array.from(input.classList) | ||
.filter(config.className); | ||
return names.map((name) => ({ | ||
name: "." + cssesc(name, { isIdentifier: true }), | ||
penalty: 1 | ||
})); | ||
} | ||
function tagName(input) { | ||
const name = input.tagName.toLowerCase(); | ||
if (config.tagName(name)) { | ||
return { | ||
name, | ||
penalty: 2 | ||
}; | ||
} | ||
return null; | ||
} | ||
function any() { | ||
return { | ||
name: "*", | ||
penalty: 3 | ||
}; | ||
} | ||
function index(input) { | ||
const parent = input.parentNode; | ||
if (!parent) { | ||
return null; | ||
} | ||
let child = parent.firstChild; | ||
if (!child) { | ||
return null; | ||
} | ||
let i = 0; | ||
while (child) { | ||
if (child.nodeType === Node.ELEMENT_NODE) { | ||
i++; | ||
} | ||
if (child === input) { | ||
break; | ||
} | ||
child = child.nextSibling; | ||
} | ||
return i; | ||
} | ||
function nthChild(node, i) { | ||
return { | ||
name: node.name + `:nth-child(${i})`, | ||
penalty: node.penalty + 1 | ||
}; | ||
} | ||
function dispensableNth(node) { | ||
return node.name !== "html" && !node.name.startsWith("#"); | ||
} | ||
function maybe(...level) { | ||
const list = level.filter(notEmpty); | ||
if (list.length > 0) { | ||
return list; | ||
} | ||
return null; | ||
} | ||
function notEmpty(value) { | ||
return value !== null && value !== undefined; | ||
} | ||
function* combinations(stack, path = []) { | ||
if (stack.length > 0) { | ||
for (let node of stack[0]) { | ||
yield* combinations(stack.slice(1, stack.length), path.concat(node)); | ||
} | ||
} | ||
else { | ||
yield path; | ||
} | ||
} | ||
function sort(paths) { | ||
return Array.from(paths).sort((a, b) => penalty(a) - penalty(b)); | ||
} | ||
function* optimize(path, input, scope = { | ||
counter: 0, | ||
visited: new Map() | ||
}) { | ||
if (path.length > 2 && path.length > config.optimizedMinLength) { | ||
for (let i = 1; i < path.length - 1; i++) { | ||
if (scope.counter > config.maxNumberOfTries) { | ||
return; // Okay At least I tried! | ||
} | ||
scope.counter += 1; | ||
const newPath = [...path]; | ||
newPath.splice(i, 1); | ||
const newPathKey = selector(newPath); | ||
if (scope.visited.has(newPathKey)) { | ||
return; | ||
} | ||
if (unique(newPath) && same(newPath, input)) { | ||
yield newPath; | ||
scope.visited.set(newPathKey, true); | ||
yield* optimize(newPath, input, scope); | ||
} | ||
} | ||
} | ||
} | ||
function same(path, input) { | ||
return rootDocument.querySelector(selector(path)) === input; | ||
} | ||
const regexAnySingleEscape = /[ -,\.\/:-@\[-\^`\{-~]/; | ||
const regexSingleEscape = /[ -,\.\/:-@\[\]\^`\{-~]/; | ||
const regexExcessiveSpaces = /(^|\\+)?(\\[A-F0-9]{1,6})\x20(?![a-fA-F0-9\x20])/g; | ||
const defaultOptions = { | ||
"escapeEverything": false, | ||
"isIdentifier": false, | ||
"quotes": "single", | ||
"wrap": false | ||
}; | ||
function cssesc(string, opt = {}) { | ||
const options = Object.assign(Object.assign({}, defaultOptions), opt); | ||
if (options.quotes != "single" && options.quotes != "double") { | ||
options.quotes = "single"; | ||
} | ||
const quote = options.quotes == "double" ? "\"" : "'"; | ||
const isIdentifier = options.isIdentifier; | ||
const firstChar = string.charAt(0); | ||
let output = ""; | ||
let counter = 0; | ||
const length = string.length; | ||
while (counter < length) { | ||
const character = string.charAt(counter++); | ||
let codePoint = character.charCodeAt(0); | ||
let value = void 0; | ||
// If it’s not a printable ASCII character… | ||
if (codePoint < 0x20 || codePoint > 0x7E) { | ||
if (codePoint >= 0xD800 && codePoint <= 0xDBFF && counter < length) { | ||
// It’s a high surrogate, and there is a next character. | ||
const extra = string.charCodeAt(counter++); | ||
if ((extra & 0xFC00) == 0xDC00) { | ||
// next character is low surrogate | ||
codePoint = ((codePoint & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000; | ||
} | ||
else { | ||
// It’s an unmatched surrogate; only append this code unit, in case | ||
// the next code unit is the high surrogate of a surrogate pair. | ||
counter--; | ||
} | ||
} | ||
value = "\\" + codePoint.toString(16).toUpperCase() + " "; | ||
} | ||
else { | ||
if (options.escapeEverything) { | ||
if (regexAnySingleEscape.test(character)) { | ||
value = "\\" + character; | ||
} | ||
else { | ||
value = "\\" + codePoint.toString(16).toUpperCase() + " "; | ||
} | ||
} | ||
else if (/[\t\n\f\r\x0B]/.test(character)) { | ||
value = "\\" + codePoint.toString(16).toUpperCase() + " "; | ||
} | ||
else if (character == "\\" || !isIdentifier && (character == "\"" && quote == character || character == "'" && quote == character) || isIdentifier && regexSingleEscape.test(character)) { | ||
value = "\\" + character; | ||
} | ||
else { | ||
value = character; | ||
} | ||
} | ||
output += value; | ||
} | ||
if (isIdentifier) { | ||
if (/^-[-\d]/.test(output)) { | ||
output = "\\-" + output.slice(1); | ||
} | ||
else if (/\d/.test(firstChar)) { | ||
output = "\\3" + firstChar + " " + output.slice(1); | ||
} | ||
} | ||
// Remove spaces after `\HEX` escapes that are not followed by a hex digit, | ||
// since they’re redundant. Note that this is only possible if the escape | ||
// sequence isn’t preceded by an odd number of backslashes. | ||
output = output.replace(regexExcessiveSpaces, function ($0, $1, $2) { | ||
if ($1 && $1.length % 2) { | ||
// It’s not safe to remove the space, so don’t. | ||
return $0; | ||
} | ||
// Strip the space. | ||
return ($1 || "") + $2; | ||
}); | ||
if (!isIdentifier && options.wrap) { | ||
return quote + output + quote; | ||
} | ||
return output; | ||
} | ||
function unwrapExports (x) { | ||
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; | ||
} | ||
return finder; | ||
function createCommonjsModule(fn, module) { | ||
return module = { exports: {} }, fn(module, module.exports), module.exports; | ||
} | ||
/*! https://mths.be/cssesc v1.0.1 by @mathias */ | ||
var object = {}; | ||
var hasOwnProperty = object.hasOwnProperty; | ||
var merge = function merge(options, defaults) { | ||
if (!options) { | ||
return defaults; | ||
} | ||
var result = {}; | ||
for (var key in defaults) { | ||
// `if (defaults.hasOwnProperty(key) { … }` is not needed here, since | ||
// only recognized option names are used. | ||
result[key] = hasOwnProperty.call(options, key) ? options[key] : defaults[key]; | ||
} | ||
return result; | ||
}; | ||
var regexAnySingleEscape = /[ -,\.\/;-@\[-\^`\{-~]/; | ||
var regexSingleEscape = /[ -,\.\/;-@\[\]\^`\{-~]/; | ||
var regexExcessiveSpaces = /(^|\\+)?(\\[A-F0-9]{1,6})\x20(?![a-fA-F0-9\x20])/g; | ||
// https://mathiasbynens.be/notes/css-escapes#css | ||
var cssesc = function cssesc(string, options) { | ||
options = merge(options, cssesc.options); | ||
if (options.quotes != 'single' && options.quotes != 'double') { | ||
options.quotes = 'single'; | ||
} | ||
var quote = options.quotes == 'double' ? '"' : '\''; | ||
var isIdentifier = options.isIdentifier; | ||
var firstChar = string.charAt(0); | ||
var output = ''; | ||
var counter = 0; | ||
var length = string.length; | ||
while (counter < length) { | ||
var character = string.charAt(counter++); | ||
var codePoint = character.charCodeAt(); | ||
var value = void 0; | ||
// If it’s not a printable ASCII character… | ||
if (codePoint < 0x20 || codePoint > 0x7E) { | ||
if (codePoint >= 0xD800 && codePoint <= 0xDBFF && counter < length) { | ||
// It’s a high surrogate, and there is a next character. | ||
var extra = string.charCodeAt(counter++); | ||
if ((extra & 0xFC00) == 0xDC00) { | ||
// next character is low surrogate | ||
codePoint = ((codePoint & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000; | ||
} else { | ||
// It’s an unmatched surrogate; only append this code unit, in case | ||
// the next code unit is the high surrogate of a surrogate pair. | ||
counter--; | ||
} | ||
} | ||
value = '\\' + codePoint.toString(16).toUpperCase() + ' '; | ||
} else { | ||
if (options.escapeEverything) { | ||
if (regexAnySingleEscape.test(character)) { | ||
value = '\\' + character; | ||
} else { | ||
value = '\\' + codePoint.toString(16).toUpperCase() + ' '; | ||
} | ||
// Note: `:` could be escaped as `\:`, but that fails in IE < 8. | ||
} else if (/[\t\n\f\r\x0B:]/.test(character)) { | ||
if (!isIdentifier && character == ':') { | ||
value = character; | ||
} else { | ||
value = '\\' + codePoint.toString(16).toUpperCase() + ' '; | ||
} | ||
} else if (character == '\\' || !isIdentifier && (character == '"' && quote == character || character == '\'' && quote == character) || isIdentifier && regexSingleEscape.test(character)) { | ||
value = '\\' + character; | ||
} else { | ||
value = character; | ||
} | ||
} | ||
output += value; | ||
} | ||
if (isIdentifier) { | ||
if (/^_/.test(output)) { | ||
// Prevent IE6 from ignoring the rule altogether (in case this is for an | ||
// identifier used as a selector) | ||
output = '\\_' + output.slice(1); | ||
} else if (/^-[-\d]/.test(output)) { | ||
output = '\\-' + output.slice(1); | ||
} else if (/\d/.test(firstChar)) { | ||
output = '\\3' + firstChar + ' ' + output.slice(1); | ||
} | ||
} | ||
// Remove spaces after `\HEX` escapes that are not followed by a hex digit, | ||
// since they’re redundant. Note that this is only possible if the escape | ||
// sequence isn’t preceded by an odd number of backslashes. | ||
output = output.replace(regexExcessiveSpaces, function ($0, $1, $2) { | ||
if ($1 && $1.length % 2) { | ||
// It’s not safe to remove the space, so don’t. | ||
return $0; | ||
} | ||
// Strip the space. | ||
return ($1 || '') + $2; | ||
}); | ||
if (!isIdentifier && options.wrap) { | ||
return quote + output + quote; | ||
} | ||
return output; | ||
}; | ||
// Expose default options (so they can be overridden globally). | ||
cssesc.options = { | ||
'escapeEverything': false, | ||
'isIdentifier': false, | ||
'quotes': 'single', | ||
'wrap': false | ||
}; | ||
cssesc.version = '1.0.1'; | ||
var cssesc_1 = cssesc; | ||
var dist = createCommonjsModule(function (module, exports) { | ||
var __assign = (commonjsGlobal && commonjsGlobal.__assign) || function () { | ||
__assign = Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
var __generator = (commonjsGlobal && commonjsGlobal.__generator) || function (thisArg, body) { | ||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; | ||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; | ||
function verb(n) { return function (v) { return step([n, v]); }; } | ||
function step(op) { | ||
if (f) throw new TypeError("Generator is already executing."); | ||
while (_) try { | ||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; | ||
if (y = 0, t) op = [op[0] & 2, t.value]; | ||
switch (op[0]) { | ||
case 0: case 1: t = op; break; | ||
case 4: _.label++; return { value: op[1], done: false }; | ||
case 5: _.label++; y = op[1]; op = [0]; continue; | ||
case 7: op = _.ops.pop(); _.trys.pop(); continue; | ||
default: | ||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } | ||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } | ||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } | ||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } | ||
if (t[2]) _.ops.pop(); | ||
_.trys.pop(); continue; | ||
} | ||
op = body.call(thisArg, _); | ||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } | ||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; | ||
} | ||
}; | ||
var __values = (commonjsGlobal && commonjsGlobal.__values) || function (o) { | ||
var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; | ||
if (m) return m.call(o); | ||
return { | ||
next: function () { | ||
if (o && i >= o.length) o = void 0; | ||
return { value: o && o[i++], done: !o }; | ||
} | ||
}; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var Limit; | ||
(function (Limit) { | ||
Limit[Limit["All"] = 0] = "All"; | ||
Limit[Limit["Two"] = 1] = "Two"; | ||
Limit[Limit["One"] = 2] = "One"; | ||
})(Limit || (Limit = {})); | ||
var config; | ||
var rootDocument; | ||
function default_1(input, options) { | ||
if (input.nodeType !== Node.ELEMENT_NODE) { | ||
throw new Error("Can't generate CSS selector for non-element node type."); | ||
} | ||
if ('html' === input.tagName.toLowerCase()) { | ||
return 'html'; | ||
} | ||
var defaults = { | ||
root: document.body, | ||
idName: function (name) { return true; }, | ||
className: function (name) { return true; }, | ||
tagName: function (name) { return true; }, | ||
attr: function (name, value) { return false; }, | ||
seedMinLength: 1, | ||
optimizedMinLength: 2, | ||
threshold: 1000, | ||
}; | ||
config = __assign({}, defaults, options); | ||
rootDocument = findRootDocument(config.root, defaults); | ||
var path = bottomUpSearch(input, Limit.All, function () { | ||
return bottomUpSearch(input, Limit.Two, function () { | ||
return bottomUpSearch(input, Limit.One); | ||
}); | ||
}); | ||
if (path) { | ||
var optimized = sort(optimize(path, input)); | ||
if (optimized.length > 0) { | ||
path = optimized[0]; | ||
} | ||
return selector(path); | ||
} | ||
else { | ||
throw new Error("Selector was not found."); | ||
} | ||
} | ||
exports.default = default_1; | ||
function findRootDocument(rootNode, defaults) { | ||
if (rootNode.nodeType === Node.DOCUMENT_NODE) { | ||
return rootNode; | ||
} | ||
if (rootNode === defaults.root) { | ||
return rootNode.ownerDocument; | ||
} | ||
return rootNode; | ||
} | ||
function bottomUpSearch(input, limit, fallback) { | ||
var path = null; | ||
var stack = []; | ||
var current = input; | ||
var i = 0; | ||
var _loop_1 = function () { | ||
var level = maybe(id(current)) || maybe.apply(void 0, attr(current)) || maybe.apply(void 0, classNames(current)) || maybe(tagName(current)) || [any()]; | ||
var nth = index(current); | ||
if (limit === Limit.All) { | ||
if (nth) { | ||
level = level.concat(level.filter(dispensableNth).map(function (node) { return nthChild(node, nth); })); | ||
} | ||
} | ||
else if (limit === Limit.Two) { | ||
level = level.slice(0, 1); | ||
if (nth) { | ||
level = level.concat(level.filter(dispensableNth).map(function (node) { return nthChild(node, nth); })); | ||
} | ||
} | ||
else if (limit === Limit.One) { | ||
var node = (level = level.slice(0, 1))[0]; | ||
if (nth && dispensableNth(node)) { | ||
level = [nthChild(node, nth)]; | ||
} | ||
} | ||
for (var _i = 0, level_1 = level; _i < level_1.length; _i++) { | ||
var node = level_1[_i]; | ||
node.level = i; | ||
} | ||
stack.push(level); | ||
if (stack.length >= config.seedMinLength) { | ||
path = findUniquePath(stack, fallback); | ||
if (path) { | ||
return "break"; | ||
} | ||
} | ||
current = current.parentElement; | ||
i++; | ||
}; | ||
while (current && current !== config.root.parentElement) { | ||
var state_1 = _loop_1(); | ||
if (state_1 === "break") | ||
break; | ||
} | ||
if (!path) { | ||
path = findUniquePath(stack, fallback); | ||
} | ||
return path; | ||
} | ||
function findUniquePath(stack, fallback) { | ||
var paths = sort(combinations(stack)); | ||
if (paths.length > config.threshold) { | ||
return fallback ? fallback() : null; | ||
} | ||
for (var _i = 0, paths_1 = paths; _i < paths_1.length; _i++) { | ||
var candidate = paths_1[_i]; | ||
if (unique(candidate)) { | ||
return candidate; | ||
} | ||
} | ||
return null; | ||
} | ||
function selector(path) { | ||
var node = path[0]; | ||
var query = node.name; | ||
for (var i = 1; i < path.length; i++) { | ||
var level = path[i].level || 0; | ||
if (node.level === level - 1) { | ||
query = path[i].name + " > " + query; | ||
} | ||
else { | ||
query = path[i].name + " " + query; | ||
} | ||
node = path[i]; | ||
} | ||
return query; | ||
} | ||
function penalty(path) { | ||
return path.map(function (node) { return node.penalty; }).reduce(function (acc, i) { return acc + i; }, 0); | ||
} | ||
function unique(path) { | ||
switch (rootDocument.querySelectorAll(selector(path)).length) { | ||
case 0: | ||
throw new Error("Can't select any node with this selector: " + selector(path)); | ||
case 1: | ||
return true; | ||
default: | ||
return false; | ||
} | ||
} | ||
function id(input) { | ||
var elementId = input.getAttribute('id'); | ||
if (elementId && config.idName(elementId)) { | ||
return { | ||
name: '#' + cssesc_1(elementId, { isIdentifier: true }), | ||
penalty: 0, | ||
}; | ||
} | ||
return null; | ||
} | ||
function attr(input) { | ||
var attrs = Array.from(input.attributes).filter(function (attr) { return config.attr(attr.name, attr.value); }); | ||
return attrs.map(function (attr) { return ({ | ||
name: '[' + cssesc_1(attr.name, { isIdentifier: true }) + '="' + cssesc_1(attr.value) + '"]', | ||
penalty: 0.5 | ||
}); }); | ||
} | ||
function classNames(input) { | ||
var names = Array.from(input.classList) | ||
.filter(config.className); | ||
return names.map(function (name) { return ({ | ||
name: '.' + cssesc_1(name, { isIdentifier: true }), | ||
penalty: 1 | ||
}); }); | ||
} | ||
function tagName(input) { | ||
var name = input.tagName.toLowerCase(); | ||
if (config.tagName(name)) { | ||
return { | ||
name: name, | ||
penalty: 2 | ||
}; | ||
} | ||
return null; | ||
} | ||
function any() { | ||
return { | ||
name: '*', | ||
penalty: 3 | ||
}; | ||
} | ||
function index(input) { | ||
var parent = input.parentNode; | ||
if (!parent) { | ||
return null; | ||
} | ||
var child = parent.firstChild; | ||
if (!child) { | ||
return null; | ||
} | ||
var i = 0; | ||
while (child) { | ||
if (child.nodeType === Node.ELEMENT_NODE) { | ||
i++; | ||
} | ||
if (child === input) { | ||
break; | ||
} | ||
child = child.nextSibling; | ||
} | ||
return i; | ||
} | ||
function nthChild(node, i) { | ||
return { | ||
name: node.name + (":nth-child(" + i + ")"), | ||
penalty: node.penalty + 1 | ||
}; | ||
} | ||
function dispensableNth(node) { | ||
return node.name !== 'html' && !node.name.startsWith('#'); | ||
} | ||
function maybe() { | ||
var level = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
level[_i] = arguments[_i]; | ||
} | ||
var list = level.filter(notEmpty); | ||
if (list.length > 0) { | ||
return list; | ||
} | ||
return null; | ||
} | ||
function notEmpty(value) { | ||
return value !== null && value !== undefined; | ||
} | ||
function combinations(stack, path) { | ||
var _i, _a, node; | ||
if (path === void 0) { path = []; } | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
if (!(stack.length > 0)) return [3 /*break*/, 5]; | ||
_i = 0, _a = stack[0]; | ||
_b.label = 1; | ||
case 1: | ||
if (!(_i < _a.length)) return [3 /*break*/, 4]; | ||
node = _a[_i]; | ||
return [5 /*yield**/, __values(combinations(stack.slice(1, stack.length), path.concat(node)))]; | ||
case 2: | ||
_b.sent(); | ||
_b.label = 3; | ||
case 3: | ||
_i++; | ||
return [3 /*break*/, 1]; | ||
case 4: return [3 /*break*/, 7]; | ||
case 5: return [4 /*yield*/, path]; | ||
case 6: | ||
_b.sent(); | ||
_b.label = 7; | ||
case 7: return [2 /*return*/]; | ||
} | ||
}); | ||
} | ||
function sort(paths) { | ||
return Array.from(paths).sort(function (a, b) { return penalty(a) - penalty(b); }); | ||
} | ||
function optimize(path, input) { | ||
var i, newPath; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
if (!(path.length > 2 && path.length > config.optimizedMinLength)) return [3 /*break*/, 5]; | ||
i = 1; | ||
_a.label = 1; | ||
case 1: | ||
if (!(i < path.length - 1)) return [3 /*break*/, 5]; | ||
newPath = path.slice(); | ||
newPath.splice(i, 1); | ||
if (!(unique(newPath) && same(newPath, input))) return [3 /*break*/, 4]; | ||
return [4 /*yield*/, newPath]; | ||
case 2: | ||
_a.sent(); | ||
return [5 /*yield**/, __values(optimize(newPath, input))]; | ||
case 3: | ||
_a.sent(); | ||
_a.label = 4; | ||
case 4: | ||
i++; | ||
return [3 /*break*/, 1]; | ||
case 5: return [2 /*return*/]; | ||
} | ||
}); | ||
} | ||
function same(path, input) { | ||
return rootDocument.querySelector(selector(path)) === input; | ||
} | ||
}); | ||
var finder = unwrapExports(dist); | ||
return finder; | ||
}()); |
@@ -1,4 +0,4 @@ | ||
import { ElementHandle } from 'puppeteer'; | ||
import { Options as FinderOptions } from '@medv/finder'; | ||
import type { ElementHandle } from 'puppeteer'; | ||
import type { Options as FinderOptions } from '@medv/finder'; | ||
export declare type Options = Partial<Omit<FinderOptions, 'root' | 'idName' | 'tagName' | 'attr'>>; | ||
export declare const element2selector: (element: ElementHandle<Element>, options?: Partial<Pick<FinderOptions, "className" | "seedMinLength" | "optimizedMinLength" | "threshold">> | undefined) => Promise<string>; | ||
export declare const element2selector: (element: ElementHandle, options?: Partial<Pick<FinderOptions, "className" | "seedMinLength" | "optimizedMinLength" | "threshold" | "maxNumberOfTries">> | undefined) => Promise<string>; |
@@ -6,2 +6,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.element2selector = void 0; | ||
const fs_1 = __importDefault(require("fs")); | ||
@@ -12,7 +13,22 @@ const path_1 = __importDefault(require("path")); | ||
${code}; | ||
return finder(node, opts); | ||
try { | ||
const selector = finder(node, opts); | ||
return { | ||
error: null, | ||
selector, | ||
}; | ||
} catch (e) { | ||
return { | ||
error: e.message, | ||
selector: null, | ||
}; | ||
} | ||
}`); | ||
exports.element2selector = async (element, options) => { | ||
const selector = await element.evaluate(evaluator, options); | ||
return selector; | ||
const element2selector = async (element, options) => { | ||
const result = (await element.evaluate(evaluator, options)); | ||
if (result.error != null) { | ||
throw new Error(result.error); | ||
} | ||
return result.selector; | ||
}; | ||
exports.element2selector = element2selector; |
{ | ||
"name": "puppeteer-element2selector", | ||
"version": "0.0.1", | ||
"version": "0.0.2", | ||
"description": "A utility to convert puppeteer ElementHandle to CSS Selector.", | ||
@@ -42,22 +42,22 @@ "main": "lib/index.js", | ||
"dependencies": { | ||
"@medv/finder": "^1.1.2" | ||
"@medv/finder": "^2.0.0" | ||
}, | ||
"devDependencies": { | ||
"@rollup/plugin-commonjs": "^11.0.2", | ||
"@rollup/plugin-node-resolve": "^7.1.1", | ||
"@types/jest": "^25.1.3", | ||
"@types/jest-environment-puppeteer": "^4.3.1", | ||
"@types/puppeteer": "^2.0.0", | ||
"husky": "^4.2.3", | ||
"jest": "^25.1.0", | ||
"@rollup/plugin-commonjs": "^16.0.0", | ||
"@rollup/plugin-node-resolve": "^10.0.0", | ||
"@types/jest": "^26.0.15", | ||
"@types/jest-environment-puppeteer": "^4.4.0", | ||
"@types/puppeteer": "^5.4.0", | ||
"husky": "^4.3.0", | ||
"jest": "^26.6.3", | ||
"jest-puppeteer": "^4.4.0", | ||
"lint-staged": "^10.0.7", | ||
"prettier": "^1.19.1", | ||
"puppeteer": "^2.1.1", | ||
"lint-staged": "^10.5.1", | ||
"prettier": "^2.2.0", | ||
"puppeteer": "^5.5.0", | ||
"rimraf": "^3.0.2", | ||
"rollup": "^1.31.1", | ||
"serve-handler": "^6.1.2", | ||
"standard-version": "^7.1.0", | ||
"ts-jest": "^25.2.1", | ||
"typescript": "^3.8.2" | ||
"rollup": "^2.33.3", | ||
"serve-handler": "^6.1.3", | ||
"standard-version": "^9.0.0", | ||
"ts-jest": "^26.4.4", | ||
"typescript": "^4.1.2" | ||
}, | ||
@@ -71,6 +71,5 @@ "husky": { | ||
"*.{js,css,md,yml,json}": [ | ||
"prettier --write", | ||
"git add" | ||
"prettier --write" | ||
] | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
19688
379
3
+ Added@medv/finder@2.1.0(transitive)
- Removed@medv/finder@1.1.3(transitive)
- Removedcssesc@2.0.0(transitive)
Updated@medv/finder@^2.0.0