index.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. "use strict";
  2. var __assign = (this && this.__assign) || function () {
  3. __assign = Object.assign || function(t) {
  4. for (var s, i = 1, n = arguments.length; i < n; i++) {
  5. s = arguments[i];
  6. for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
  7. t[p] = s[p];
  8. }
  9. return t;
  10. };
  11. return __assign.apply(this, arguments);
  12. };
  13. var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
  14. if (k2 === undefined) k2 = k;
  15. var desc = Object.getOwnPropertyDescriptor(m, k);
  16. if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
  17. desc = { enumerable: true, get: function() { return m[k]; } };
  18. }
  19. Object.defineProperty(o, k2, desc);
  20. }) : (function(o, m, k, k2) {
  21. if (k2 === undefined) k2 = k;
  22. o[k2] = m[k];
  23. }));
  24. var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
  25. Object.defineProperty(o, "default", { enumerable: true, value: v });
  26. }) : function(o, v) {
  27. o["default"] = v;
  28. });
  29. var __importStar = (this && this.__importStar) || function (mod) {
  30. if (mod && mod.__esModule) return mod;
  31. var result = {};
  32. if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
  33. __setModuleDefault(result, mod);
  34. return result;
  35. };
  36. var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
  37. if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
  38. if (ar || !(i in from)) {
  39. if (!ar) ar = Array.prototype.slice.call(from, 0, i);
  40. ar[i] = from[i];
  41. }
  42. }
  43. return to.concat(ar || Array.prototype.slice.call(from));
  44. };
  45. Object.defineProperty(exports, "__esModule", { value: true });
  46. exports.select = exports.filter = exports.some = exports.is = exports.aliases = exports.pseudos = exports.filters = void 0;
  47. var css_what_1 = require("css-what");
  48. var css_select_1 = require("css-select");
  49. var DomUtils = __importStar(require("domutils"));
  50. var helpers_1 = require("./helpers");
  51. var positionals_1 = require("./positionals");
  52. // Re-export pseudo extension points
  53. var css_select_2 = require("css-select");
  54. Object.defineProperty(exports, "filters", { enumerable: true, get: function () { return css_select_2.filters; } });
  55. Object.defineProperty(exports, "pseudos", { enumerable: true, get: function () { return css_select_2.pseudos; } });
  56. Object.defineProperty(exports, "aliases", { enumerable: true, get: function () { return css_select_2.aliases; } });
  57. /** Used to indicate a scope should be filtered. Might be ignored when filtering. */
  58. var SCOPE_PSEUDO = {
  59. type: css_what_1.SelectorType.Pseudo,
  60. name: "scope",
  61. data: null,
  62. };
  63. /** Used for actually filtering for scope. */
  64. var CUSTOM_SCOPE_PSEUDO = __assign({}, SCOPE_PSEUDO);
  65. var UNIVERSAL_SELECTOR = {
  66. type: css_what_1.SelectorType.Universal,
  67. namespace: null,
  68. };
  69. function is(element, selector, options) {
  70. if (options === void 0) { options = {}; }
  71. return some([element], selector, options);
  72. }
  73. exports.is = is;
  74. function some(elements, selector, options) {
  75. if (options === void 0) { options = {}; }
  76. if (typeof selector === "function")
  77. return elements.some(selector);
  78. var _a = (0, helpers_1.groupSelectors)((0, css_what_1.parse)(selector)), plain = _a[0], filtered = _a[1];
  79. return ((plain.length > 0 && elements.some((0, css_select_1._compileToken)(plain, options))) ||
  80. filtered.some(function (sel) { return filterBySelector(sel, elements, options).length > 0; }));
  81. }
  82. exports.some = some;
  83. function filterByPosition(filter, elems, data, options) {
  84. var num = typeof data === "string" ? parseInt(data, 10) : NaN;
  85. switch (filter) {
  86. case "first":
  87. case "lt":
  88. // Already done in `getLimit`
  89. return elems;
  90. case "last":
  91. return elems.length > 0 ? [elems[elems.length - 1]] : elems;
  92. case "nth":
  93. case "eq":
  94. return isFinite(num) && Math.abs(num) < elems.length
  95. ? [num < 0 ? elems[elems.length + num] : elems[num]]
  96. : [];
  97. case "gt":
  98. return isFinite(num) ? elems.slice(num + 1) : [];
  99. case "even":
  100. return elems.filter(function (_, i) { return i % 2 === 0; });
  101. case "odd":
  102. return elems.filter(function (_, i) { return i % 2 === 1; });
  103. case "not": {
  104. var filtered_1 = new Set(filterParsed(data, elems, options));
  105. return elems.filter(function (e) { return !filtered_1.has(e); });
  106. }
  107. }
  108. }
  109. function filter(selector, elements, options) {
  110. if (options === void 0) { options = {}; }
  111. return filterParsed((0, css_what_1.parse)(selector), elements, options);
  112. }
  113. exports.filter = filter;
  114. /**
  115. * Filter a set of elements by a selector.
  116. *
  117. * Will return elements in the original order.
  118. *
  119. * @param selector Selector to filter by.
  120. * @param elements Elements to filter.
  121. * @param options Options for selector.
  122. */
  123. function filterParsed(selector, elements, options) {
  124. if (elements.length === 0)
  125. return [];
  126. var _a = (0, helpers_1.groupSelectors)(selector), plainSelectors = _a[0], filteredSelectors = _a[1];
  127. var found;
  128. if (plainSelectors.length) {
  129. var filtered = filterElements(elements, plainSelectors, options);
  130. // If there are no filters, just return
  131. if (filteredSelectors.length === 0) {
  132. return filtered;
  133. }
  134. // Otherwise, we have to do some filtering
  135. if (filtered.length) {
  136. found = new Set(filtered);
  137. }
  138. }
  139. for (var i = 0; i < filteredSelectors.length && (found === null || found === void 0 ? void 0 : found.size) !== elements.length; i++) {
  140. var filteredSelector = filteredSelectors[i];
  141. var missing = found
  142. ? elements.filter(function (e) { return DomUtils.isTag(e) && !found.has(e); })
  143. : elements;
  144. if (missing.length === 0)
  145. break;
  146. var filtered = filterBySelector(filteredSelector, elements, options);
  147. if (filtered.length) {
  148. if (!found) {
  149. /*
  150. * If we haven't found anything before the last selector,
  151. * just return what we found now.
  152. */
  153. if (i === filteredSelectors.length - 1) {
  154. return filtered;
  155. }
  156. found = new Set(filtered);
  157. }
  158. else {
  159. filtered.forEach(function (el) { return found.add(el); });
  160. }
  161. }
  162. }
  163. return typeof found !== "undefined"
  164. ? (found.size === elements.length
  165. ? elements
  166. : // Filter elements to preserve order
  167. elements.filter(function (el) {
  168. return found.has(el);
  169. }))
  170. : [];
  171. }
  172. function filterBySelector(selector, elements, options) {
  173. var _a;
  174. if (selector.some(css_what_1.isTraversal)) {
  175. /*
  176. * Get root node, run selector with the scope
  177. * set to all of our nodes.
  178. */
  179. var root = (_a = options.root) !== null && _a !== void 0 ? _a : (0, helpers_1.getDocumentRoot)(elements[0]);
  180. var sel = __spreadArray(__spreadArray([], selector, true), [CUSTOM_SCOPE_PSEUDO], false);
  181. return findFilterElements(root, sel, options, true, elements);
  182. }
  183. // Performance optimization: If we don't have to traverse, just filter set.
  184. return findFilterElements(elements, selector, options, false);
  185. }
  186. function select(selector, root, options) {
  187. if (options === void 0) { options = {}; }
  188. if (typeof selector === "function") {
  189. return find(root, selector);
  190. }
  191. var _a = (0, helpers_1.groupSelectors)((0, css_what_1.parse)(selector)), plain = _a[0], filtered = _a[1];
  192. var results = filtered.map(function (sel) {
  193. return findFilterElements(root, sel, options, true);
  194. });
  195. // Plain selectors can be queried in a single go
  196. if (plain.length) {
  197. results.push(findElements(root, plain, options, Infinity));
  198. }
  199. if (results.length === 0) {
  200. return [];
  201. }
  202. // If there was only a single selector, just return the result
  203. if (results.length === 1) {
  204. return results[0];
  205. }
  206. // Sort results, filtering for duplicates
  207. return DomUtils.uniqueSort(results.reduce(function (a, b) { return __spreadArray(__spreadArray([], a, true), b, true); }));
  208. }
  209. exports.select = select;
  210. // Traversals that are treated differently in css-select.
  211. var specialTraversal = new Set([
  212. css_what_1.SelectorType.Descendant,
  213. css_what_1.SelectorType.Adjacent,
  214. ]);
  215. function includesScopePseudo(t) {
  216. return (t !== SCOPE_PSEUDO &&
  217. t.type === "pseudo" &&
  218. (t.name === "scope" ||
  219. (Array.isArray(t.data) &&
  220. t.data.some(function (data) { return data.some(includesScopePseudo); }))));
  221. }
  222. function addContextIfScope(selector, options, scopeContext) {
  223. return scopeContext && selector.some(includesScopePseudo)
  224. ? __assign(__assign({}, options), { context: scopeContext }) : options;
  225. }
  226. /**
  227. *
  228. * @param root Element(s) to search from.
  229. * @param selector Selector to look for.
  230. * @param options Options for querying.
  231. * @param queryForSelector Query multiple levels deep for the initial selector, even if it doesn't contain a traversal.
  232. * @param scopeContext Optional context for a :scope.
  233. */
  234. function findFilterElements(root, selector, options, queryForSelector, scopeContext) {
  235. var filterIndex = selector.findIndex(positionals_1.isFilter);
  236. var sub = selector.slice(0, filterIndex);
  237. var filter = selector[filterIndex];
  238. /*
  239. * Set the number of elements to retrieve.
  240. * Eg. for :first, we only have to get a single element.
  241. */
  242. var limit = (0, positionals_1.getLimit)(filter.name, filter.data);
  243. if (limit === 0)
  244. return [];
  245. var subOpts = addContextIfScope(sub, options, scopeContext);
  246. /*
  247. * Skip `findElements` call if our selector starts with a positional
  248. * pseudo.
  249. */
  250. var elemsNoLimit = sub.length === 0 && !Array.isArray(root)
  251. ? DomUtils.getChildren(root).filter(DomUtils.isTag)
  252. : sub.length === 0 || (sub.length === 1 && sub[0] === SCOPE_PSEUDO)
  253. ? (Array.isArray(root) ? root : [root]).filter(DomUtils.isTag)
  254. : queryForSelector || sub.some(css_what_1.isTraversal)
  255. ? findElements(root, [sub], subOpts, limit)
  256. : filterElements(root, [sub], subOpts);
  257. var elems = elemsNoLimit.slice(0, limit);
  258. var result = filterByPosition(filter.name, elems, filter.data, options);
  259. if (result.length === 0 || selector.length === filterIndex + 1) {
  260. return result;
  261. }
  262. var remainingSelector = selector.slice(filterIndex + 1);
  263. var remainingHasTraversal = remainingSelector.some(css_what_1.isTraversal);
  264. var remainingOpts = addContextIfScope(remainingSelector, options, scopeContext);
  265. if (remainingHasTraversal) {
  266. /*
  267. * Some types of traversals have special logic when they start a selector
  268. * in css-select. If this is the case, add a universal selector in front of
  269. * the selector to avoid this behavior.
  270. */
  271. if (specialTraversal.has(remainingSelector[0].type)) {
  272. remainingSelector.unshift(UNIVERSAL_SELECTOR);
  273. }
  274. /*
  275. * Add a scope token in front of the remaining selector,
  276. * to make sure traversals don't match elements that aren't a
  277. * part of the considered tree.
  278. */
  279. remainingSelector.unshift(SCOPE_PSEUDO);
  280. }
  281. /*
  282. * If we have another filter, recursively call `findFilterElements`,
  283. * with the `recursive` flag disabled. We only have to look for more
  284. * elements when we see a traversal.
  285. *
  286. * Otherwise,
  287. */
  288. return remainingSelector.some(positionals_1.isFilter)
  289. ? findFilterElements(result, remainingSelector, options, false, scopeContext)
  290. : remainingHasTraversal
  291. ? // Query existing elements to resolve traversal.
  292. findElements(result, [remainingSelector], remainingOpts, Infinity)
  293. : // If we don't have any more traversals, simply filter elements.
  294. filterElements(result, [remainingSelector], remainingOpts);
  295. }
  296. function findElements(root, sel, options, limit) {
  297. if (limit === 0)
  298. return [];
  299. var query = (0, css_select_1._compileToken)(sel, options, root);
  300. return find(root, query, limit);
  301. }
  302. function find(root, query, limit) {
  303. if (limit === void 0) { limit = Infinity; }
  304. var elems = (0, css_select_1.prepareContext)(root, DomUtils, query.shouldTestNextSiblings);
  305. return DomUtils.find(function (node) { return DomUtils.isTag(node) && query(node); }, elems, true, limit);
  306. }
  307. function filterElements(elements, sel, options) {
  308. var els = (Array.isArray(elements) ? elements : [elements]).filter(DomUtils.isTag);
  309. if (els.length === 0)
  310. return els;
  311. var query = (0, css_select_1._compileToken)(sel, options);
  312. return els.filter(query);
  313. }