Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Use cheerio-select, to support positional selectors #1565

Merged
merged 2 commits into from
Dec 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
},
"plugins": ["jsdoc"],
"extends": ["eslint:recommended", "prettier"],
"globals": {},
"globals": { "Set": true },
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Set has been supported since node 0.12. The minimum node version has been bumped to support this.

"rules": {
"no-eq-null": 0,
"no-proto": 2,
Expand Down
96 changes: 53 additions & 43 deletions lib/api/traversing.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
exports = exports; // eslint-disable-line no-self-assign
// The preceeding statement is necessary for proper documentation generation.

var select = require('css-select');
var select = require('cheerio-select');
var utils = require('../utils');
var domEach = utils.domEach;
var uniqueSort = require('htmlparser2').DomUtils.uniqueSort;
Expand Down Expand Up @@ -57,7 +57,7 @@ exports.find = function (selectorOrHaystack) {

var options = { __proto__: this.options, context: this.toArray() };

return this._make(select.selectAll(selectorOrHaystack || '', elems, options));
return this._make(select.select(selectorOrHaystack || '', elems, options));
};

/**
Expand Down Expand Up @@ -151,7 +151,7 @@ exports.parentsUntil = function (selector, filter) {
var untilNodes;

if (typeof selector === 'string') {
untilNode = select.selectAll(
untilNode = select.select(
selector,
this.parents().toArray(),
this.options
Expand Down Expand Up @@ -185,7 +185,7 @@ exports.parentsUntil = function (selector, filter) {
}, this);

return this._make(
filter ? select.selectAll(filter, parentNodes, this.options) : parentNodes
filter ? select.select(filter, parentNodes, this.options) : parentNodes
);
};

Expand Down Expand Up @@ -318,11 +318,7 @@ exports.nextUntil = function (selector, filterSelector) {
var untilNodes;

if (typeof selector === 'string') {
untilNode = select.selectAll(
selector,
this.nextAll().get(),
this.options
)[0];
untilNode = select.select(selector, this.nextAll().get(), this.options)[0];
} else if (selector && selector.cheerio) {
untilNodes = selector.get();
} else if (selector) {
Expand Down Expand Up @@ -440,11 +436,7 @@ exports.prevUntil = function (selector, filterSelector) {
var untilNodes;

if (typeof selector === 'string') {
untilNode = select.selectAll(
selector,
this.prevAll().get(),
this.options
)[0];
untilNode = select.select(selector, this.prevAll().get(), this.options)[0];
} else if (selector && selector.cheerio) {
untilNodes = selector.get();
} else if (selector) {
Expand Down Expand Up @@ -606,28 +598,18 @@ exports.map = function (fn) {
return this._make(elems);
};

var makeFilterMethod = function (filterFn) {
return function (match, container) {
var testFn;
container = container || this;

if (typeof match === 'string') {
testFn = select.compile(match, container.options);
} else if (typeof match === 'function') {
testFn = function (el, i) {
return match.call(el, i, el);
};
} else if (match.cheerio) {
testFn = match.is.bind(match);
} else {
testFn = function (el) {
return match === el;
};
}

return container._make(filterFn(this, testFn));
function getFilterFn(match) {
if (typeof match === 'function') {
return function (el, i) {
return match.call(el, i, el);
};
} else if (match.cheerio) {
return match.is.bind(match);
}
return function (el) {
return match === el;
};
};
}

/**
* Iterates over a cheerio object, reducing the set of selector elements to
Expand All @@ -639,6 +621,8 @@ var makeFilterMethod = function (filterFn) {
* refers to the current element.
*
* @function
* @param {string | Function} match - Value to look for, following the rules above.
* @param {node[]} container - Optional node to filter instead.
*
* @example <caption>Selector</caption>
*
Expand All @@ -655,9 +639,18 @@ var makeFilterMethod = function (filterFn) {
*
* @see {@link http://api.jquery.com/filter/}
*/
exports.filter = makeFilterMethod(function (elements, test) {
return (elements.toArray ? elements.toArray() : elements).filter(test);
});
exports.filter = function (match, container) {
container = container || this;
var elements = this.toArray ? this.toArray() : this;

if (typeof match === 'string') {
elements = select.filter(match, elements, container.options);
} else {
elements = elements.filter(getFilterFn(match));
}

return container._make(elements);
};

/**
* Remove elements from the set of matched elements. Given a jQuery object that
Expand All @@ -670,6 +663,8 @@ exports.filter = makeFilterMethod(function (elements, test) {
* are included.
*
* @function
* @param {string | Function} match - Value to look for, following the rules above.
* @param {node[]} container - Optional node to filter instead.
*
* @example <caption>Selector</caption>
*
Expand All @@ -686,11 +681,26 @@ exports.filter = makeFilterMethod(function (elements, test) {
*
* @see {@link http://api.jquery.com/not/}
*/
exports.not = makeFilterMethod(function (elements, test) {
return elements.toArray().filter(function (item, index) {
return test(item, index) === false;
});
});
exports.not = function (match, container) {
container = container || this;
var elements = container.toArray ? container.toArray() : container;
var matches;
var filterFn;

if (typeof match === 'string') {
matches = new Set(select.filter(match, elements, this.options));
elements = elements.filter(function (el) {
return !matches.has(el);
});
} else {
filterFn = getFilterFn(match);
elements = elements.filter(function (el, i) {
return !filterFn(el, i);
});
}

return container._make(elements);
};

/**
* Filters the set of matched elements to only those which have the given DOM
Expand Down
2 changes: 1 addition & 1 deletion lib/static.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ exports = exports; // eslint-disable-line no-self-assign
var serialize = require('dom-serializer').default;
var defaultOptions = require('./options').default;
var flattenOptions = require('./options').flatten;
var select = require('css-select').selectAll;
var select = require('cheerio-select').select;
var parse5 = require('parse5');
var parse = require('./parse');

Expand Down
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@
"lib"
],
"engines": {
"node": ">= 0.6"
"node": ">= 0.12"
},
"dependencies": {
"css-select": "^3.1.2",
"cheerio-select": "github:cheeriojs/cheerio-select",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a Github reference here, until cheeriojs/cheerio-select#2 is resolved.

"dom-serializer": "~1.2.0",
"entities": "~2.1.0",
"htmlparser2": "^6.0.0",
Expand Down
2 changes: 1 addition & 1 deletion test/api/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ describe('$(...)', function () {
expect($el.data()).to.eql({
a: 'a',
b: 'b-modified',
c: 'c'
c: 'c',
});
});

Expand Down
12 changes: 7 additions & 5 deletions test/cheerio.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,13 @@ describe('cheerio', function () {
expect($fruits[0].attribs.id).to.equal('fruits');
});

it('should select first element cheerio(:first)');
// var $elem = cheerio(':first', fruits);
// var $h2 = cheerio('<h2>fruits</h2>');
// console.log($elem.before('hi'));
// console.log($elem.before($h2));
it('should select first element cheerio(:first)', function () {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test that has been skipped for forever is now finally live 😄

var $elem = cheerio('li:first', fruits);
expect($elem.attr('class')).to.equal('apple');

var $filtered = cheerio('li', fruits).filter(':even');
expect($filtered).to.have.length(2);
});

it('should be able to select immediate children: cheerio("#fruits > .pear")', function () {
var $food = cheerio(food);
Expand Down