From 69ae30875d10dc8af3f8f087ca60824be0701f96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20B=C3=B6hm?= Date: Thu, 24 Dec 2020 10:38:22 +0100 Subject: [PATCH] refactor: Declare vars when used, streamline code (#1588) * Remove unnecessary branches * Improve comments * Declare vars when used * Use `function` statements * Use `domEach` where appropriate * Disallow shadowing, use before define --- .eslintrc.json | 2 + benchmark/suite.js | 18 ++---- lib/api/attributes.js | 132 +++++++++++++++------------------------ lib/api/forms.js | 3 +- lib/api/manipulation.js | 108 +++++++++++--------------------- lib/api/traversing.js | 59 ++++++++--------- lib/cheerio.js | 20 +++--- lib/parse.js | 41 +++++------- lib/static.js | 17 +++-- test/api/deprecated.js | 10 +-- test/api/manipulation.js | 93 ++++++++++----------------- test/api/utils.js | 6 +- test/cheerio.js | 10 ++- test/xml.js | 8 +-- types/index.d.ts | 2 +- 15 files changed, 204 insertions(+), 325 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index de402a4593..163436541d 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -11,6 +11,8 @@ "curly": [2, "multi-line"], "one-var": [2, "never"], "no-else-return": 2, + "no-shadow": 2, + "no-use-before-define": [2, "nofunc"], "jsdoc/check-param-names": 2, "jsdoc/check-tag-names": 2, diff --git a/benchmark/suite.js b/benchmark/suite.js index 59d1c5e193..47c2114f54 100644 --- a/benchmark/suite.js +++ b/benchmark/suite.js @@ -25,13 +25,11 @@ Suites.prototype.cheerioOnly = function () { }; Suites.prototype.add = function (name, fileName, options) { - var markup; - var suite; if (!filterRe.test(name)) { return; } - markup = fs.readFileSync(path.join(documentDir, fileName), 'utf8'); - suite = new Benchmark.Suite(name); + var markup = fs.readFileSync(path.join(documentDir, fileName), 'utf8'); + var suite = new Benchmark.Suite(name); suite.on('start', function () { console.log('Test: ' + name + ' (file: ' + fileName + ')'); @@ -70,10 +68,8 @@ Suites.prototype._benchJsDom = function (suite, markup, options) { jQueryScript.runInContext(dom.getInternalVMContext()); - var setupData; - if (options.setup) { - setupData = options.setup.call(null, dom.window.$); - } + var setupData = options.setup && options.setup.call(null, dom.window.$); + suite.add('jsdom', function () { testFn.call(null, dom.window.$, setupData); }); @@ -83,10 +79,8 @@ Suites.prototype._benchJsDom = function (suite, markup, options) { Suites.prototype._benchCheerio = function (suite, markup, options) { var $ = cheerio.load(markup); var testFn = options.test; - var setupData; - if (options.setup) { - setupData = options.setup.call(null, $); - } + var setupData = options.setup && options.setup.call(null, $); + suite.add('cheerio', function () { testFn.call(null, $, setupData); }); diff --git a/lib/api/attributes.js b/lib/api/attributes.js index 07162d1903..6d46aaad0f 100644 --- a/lib/api/attributes.js +++ b/lib/api/attributes.js @@ -25,7 +25,7 @@ var rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hid // Matches strings that look like JSON objects or arrays var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/; -var getAttr = function (elem, name) { +function getAttr(elem, name) { if (!elem || !isTag(elem)) return; if (!elem.attribs) { @@ -55,15 +55,15 @@ var getAttr = function (elem, name) { ) { return 'on'; } -}; +} -var setAttr = function (el, name, value) { +function setAttr(el, name, value) { if (value === null) { removeAttribute(el, name); } else { el.attribs[name] = value + ''; } -}; +} /** * Method for getting and setting attributes. Gets the attribute value for only @@ -109,7 +109,7 @@ exports.attr = function (name, value) { return getAttr(this[0], name); }; -var getProp = function (el, name) { +function getProp(el, name) { if (!el || !isTag(el)) return; return name in el @@ -117,15 +117,15 @@ var getProp = function (el, name) { : rboolean.test(name) ? getAttr(el, name) !== undefined : getAttr(el, name); -}; +} -var setProp = function (el, name, value) { +function setProp(el, name, value) { if (name in el) { el[name] = value; } else { setAttr(el, name, rboolean.test(name) ? (value ? '' : null) : value); } -}; +} /** * Method for getting and setting properties. Gets the property value for only @@ -145,21 +145,19 @@ var setProp = function (el, name, value) { * @see {@link http://api.jquery.com/prop/} */ exports.prop = function (name, value) { - var i = 0; - var property; - if (typeof name === 'string' && value === undefined) { switch (name) { - case 'style': - property = this.css(); - - Object.keys(property).forEach(function (p) { - property[i++] = p; + case 'style': { + var property = this.css(); + var keys = Object.keys(property); + keys.forEach(function (p, i) { + property[i] = p; }); - property.length = i; + property.length = keys.length; break; + } case 'tagName': case 'nodeName': property = this[0].name.toUpperCase(); @@ -199,7 +197,7 @@ exports.prop = function (name, value) { } }; -var setData = function (el, name, value) { +function setData(el, name, value) { if (!el.data) { el.data = {}; } @@ -208,21 +206,16 @@ var setData = function (el, name, value) { if (typeof name === 'string' && value !== undefined) { el.data[name] = value; } -}; +} // Read the specified attribute from the equivalent HTML5 `data-*` attribute, // and (if present) cache the value in the node's internal data store. If no // attribute name is specified, read *all* HTML5 `data-*` attributes in this // manner. -var readData = function (el, name) { +function readData(el, name) { var readAll = arguments.length === 1; var domNames; - var domName; var jsNames; - var jsName; - var value; - var idx; - var length; if (readAll) { domNames = Object.keys(el.attribs).filter(function (attrName) { @@ -236,9 +229,10 @@ var readData = function (el, name) { jsNames = [name]; } - for (idx = 0, length = domNames.length; idx < length; ++idx) { - domName = domNames[idx]; - jsName = jsNames[idx]; + for (var idx = 0; idx < domNames.length; ++idx) { + var domName = domNames[idx]; + var jsName = jsNames[idx]; + var value; if (hasOwn.call(el.attribs, domName) && !hasOwn.call(el.data, jsName)) { value = el.attribs[domName]; @@ -259,7 +253,7 @@ var readData = function (el, name) { } return readAll ? el.data : value; -}; +} /** * Method for getting and setting data attributes. Gets or sets the data @@ -334,20 +328,11 @@ exports.val = function (value) { switch (element.name) { case 'textarea': return this.text(value); - case 'input': - if (querying) { - return this.attr('value'); - } - - this.attr('value', value); - return this; - - case 'select': + case 'select': { var option = this.find('option:selected'); - var returnValue; - if (option === undefined) return undefined; + if (!option) return undefined; if (!querying) { - if (!hasOwn.call(this.attr(), 'multiple') && typeof value == 'object') { + if (this.attr('multiple') == null && typeof value == 'object') { return this; } if (typeof value != 'object') { @@ -359,20 +344,16 @@ exports.val = function (value) { } return this; } - returnValue = option.attr('value'); - if (hasOwn.call(this.attr(), 'multiple')) { - returnValue = []; - domEach(option, function (__, el) { - returnValue.push(getAttr(el, 'value')); - }); - } - return returnValue; + + return this.attr('multiple') + ? option.toArray().map(function (el) { + return getAttr(el, 'value'); + }) + : option.attr('value'); + } + case 'input': case 'option': - if (!querying) { - this.attr('value', value); - return this; - } - return this.attr('value'); + return querying ? this.attr('value') : this.attr('value', value); } }; @@ -383,11 +364,11 @@ exports.val = function (value) { * @param {node} elem - Node to remove attribute from. * @param {string} name - Name of the attribute to remove. */ -var removeAttribute = function (elem, name) { +function removeAttribute(elem, name) { if (!elem.attribs || !hasOwn.call(elem.attribs, name)) return; delete elem.attribs[name]; -}; +} /** * Splits a space-separated list of names to individual @@ -396,9 +377,9 @@ var removeAttribute = function (elem, name) { * @param {string} names - Names to split. * @returns {string[]} - Split names. */ -var splitNames = function (names) { +function splitNames(names) { return names ? names.trim().split(rspace) : []; -}; +} /** * Method for removing attributes by `name`. @@ -443,19 +424,18 @@ exports.removeAttr = function (name) { * //=> true * * @param {string} className - Name of the class. + * @returns {boolean} * * @see {@link http://api.jquery.com/hasClass/} */ exports.hasClass = function (className) { return this.toArray().some(function (elem) { - var attrs = elem.attribs; - var clazz = attrs && attrs['class']; + var clazz = elem.attribs && elem.attribs['class']; var idx = -1; - var end; if (clazz && className.length) { while ((idx = clazz.indexOf(className, idx + 1)) > -1) { - end = idx + className.length; + var end = idx + className.length; if ( (idx === 0 || rspace.test(clazz[idx - 1])) && @@ -505,17 +485,14 @@ exports.addClass = function (value) { // If we don't already have classes var className = getAttr(this[i], 'class'); - var numClasses; - var setClass; if (!className) { setAttr(this[i], 'class', classNames.join(' ').trim()); } else { - setClass = ' ' + className + ' '; - numClasses = classNames.length; + var setClass = ' ' + className + ' '; // Check if class already exists - for (var j = 0; j < numClasses; j++) { + for (var j = 0; j < classNames.length; j++) { var appendClass = classNames[j] + ' '; if (setClass.indexOf(' ' + appendClass) < 0) setClass += appendClass; } @@ -544,10 +521,6 @@ exports.addClass = function (value) { * @see {@link http://api.jquery.com/removeClass/} */ exports.removeClass = function (value) { - var classes; - var numClasses; - var removeAll; - // Handle if value is a function if (typeof value === 'function') { return domEach(this, function (i, el) { @@ -558,9 +531,9 @@ exports.removeClass = function (value) { }); } - classes = splitNames(value); - numClasses = classes.length; - removeAll = arguments.length === 0; + var classes = splitNames(value); + var numClasses = classes.length; + var removeAll = arguments.length === 0; return domEach(this, function (i, el) { if (!isTag(el)) return; @@ -570,11 +543,10 @@ exports.removeClass = function (value) { el.attribs.class = ''; } else { var elClasses = splitNames(el.attribs.class); - var index; - var changed; + var changed = false; for (var j = 0; j < numClasses; j++) { - index = elClasses.indexOf(classes[j]); + var index = elClasses.indexOf(classes[j]); if (index >= 0) { elClasses.splice(index, 1); @@ -629,19 +601,17 @@ exports.toggleClass = function (value, stateVal) { var numClasses = classNames.length; var state = typeof stateVal === 'boolean' ? (stateVal ? 1 : -1) : 0; var numElements = this.length; - var elementClasses; - var index; for (var i = 0; i < numElements; i++) { // If selected element isn't a tag, move on if (!isTag(this[i])) continue; - elementClasses = splitNames(this[i].attribs.class); + var elementClasses = splitNames(this[i].attribs.class); // Check if class already exists for (var j = 0; j < numClasses; j++) { // Check if the class name is currently defined - index = elementClasses.indexOf(classNames[j]); + var index = elementClasses.indexOf(classNames[j]); // Add if stateValue === true or we are toggling and there is no value if (state >= 0 && index < 0) { diff --git a/lib/api/forms.js b/lib/api/forms.js index 161a4320d9..3e1cd8bac7 100644 --- a/lib/api/forms.js +++ b/lib/api/forms.js @@ -38,8 +38,7 @@ exports.serialize = function () { exports.serializeArray = function () { // Resolve all form elements from either forms or collections of form elements var Cheerio = this.constructor; - return this.map(function () { - var elem = this; + return this.map(function (i, elem) { var $elem = Cheerio(elem); if (elem.name === 'form') { return $elem.find(submittableSelector).toArray(); diff --git a/lib/api/manipulation.js b/lib/api/manipulation.js index d17f0dda1a..c9b349e561 100644 --- a/lib/api/manipulation.js +++ b/lib/api/manipulation.js @@ -42,26 +42,22 @@ exports._makeDomArray = function makeDomArray(elem, clone) { return clone ? cloneDom([elem]) : [elem]; }; -var _insert = function (concatenator) { +function _insert(concatenator) { return function () { var elems = slice.call(arguments); var lastIdx = this.length - 1; return domEach(this, function (i, el) { - var dom; - var domSrc; + var domSrc = + typeof elems[0] === 'function' + ? elems[0].call(el, i, html(el.children)) + : elems; - if (typeof elems[0] === 'function') { - domSrc = elems[0].call(el, i, html(el.children)); - } else { - domSrc = elems; - } - - dom = this._makeDomArray(domSrc, i < lastIdx); + var dom = this._makeDomArray(domSrc, i < lastIdx); concatenator(dom, el.children, el); }); }; -}; +} /* * Modify an array in-place, removing some number of elements and adding new @@ -74,22 +70,17 @@ var _insert = function (concatenator) { * * @private */ -var uniqueSplice = function (array, spliceIdx, spliceCount, newElems, parent) { +function uniqueSplice(array, spliceIdx, spliceCount, newElems, parent) { var spliceArgs = [spliceIdx, spliceCount].concat(newElems); var prev = array[spliceIdx - 1] || null; var next = array[spliceIdx + spliceCount] || null; - var idx; - var len; - var prevIdx; - var node; - var oldParent; // Before splicing in new elements, ensure they do not already appear in the // current array. - for (idx = 0, len = newElems.length; idx < len; ++idx) { - node = newElems[idx]; - oldParent = node.parent; - prevIdx = oldParent && oldParent.children.indexOf(newElems[idx]); + for (var idx = 0; idx < newElems.length; ++idx) { + var node = newElems[idx]; + var oldParent = node.parent; + var prevIdx = oldParent && oldParent.children.indexOf(newElems[idx]); if (oldParent && prevIdx > -1) { oldParent.children.splice(prevIdx, 1); @@ -119,7 +110,7 @@ var uniqueSplice = function (array, spliceIdx, spliceCount, newElems, parent) { next.prev = newElems[newElems.length - 1]; } return array.splice.apply(array, spliceArgs); -}; +} /** * Insert every element in the set of matched elements to the end of the @@ -241,9 +232,6 @@ function _wrap(insert) { for (var i = 0; i < this.length; i++) { var el = this[i]; - var wrapperDom; - var elInsertLocation; - var j; if (wrapperFn) { wrapper = wrapperFn.call(el, i); @@ -253,11 +241,11 @@ function _wrap(insert) { wrapper = lastParent.find(wrapper).clone(); } - wrapperDom = this._makeDomArray(wrapper, i < lastIdx).slice(0, 1); - elInsertLocation = wrapperDom[0]; + var wrapperDom = this._makeDomArray(wrapper, i < lastIdx).slice(0, 1); + var elInsertLocation = wrapperDom[0]; // Find the deepest child. Only consider the first tag child of each node // (ignore text); stop if no children are found. - j = 0; + var j = 0; while (elInsertLocation && elInsertLocation.children) { if (j >= elInsertLocation.children.length) { @@ -395,7 +383,7 @@ exports.after = function () { var elems = slice.call(arguments); var lastIdx = this.length - 1; - domEach(this, function (i, el) { + return domEach(this, function (i, el) { var parent = el.parent; if (!parent) { return; @@ -403,24 +391,20 @@ exports.after = function () { var siblings = parent.children; var index = siblings.indexOf(el); - var domSrc; - var dom; // If not found, move on if (index < 0) return; - if (typeof elems[0] === 'function') { - domSrc = elems[0].call(el, i, html(el.children)); - } else { - domSrc = elems; - } - dom = this._makeDomArray(domSrc, i < lastIdx); + var domSrc = + typeof elems[0] === 'function' + ? elems[0].call(el, i, html(el.children)) + : elems; + + var dom = this._makeDomArray(domSrc, i < lastIdx); // Add element after `this` element uniqueSplice(siblings, index + 1, 0, dom, parent); }); - - return this; }; /** @@ -494,7 +478,7 @@ exports.before = function () { var elems = slice.call(arguments); var lastIdx = this.length - 1; - domEach(this, function (i, el) { + return domEach(this, function (i, el) { var parent = el.parent; if (!parent) { return; @@ -502,25 +486,20 @@ exports.before = function () { var siblings = parent.children; var index = siblings.indexOf(el); - var domSrc; - var dom; // If not found, move on if (index < 0) return; - if (typeof elems[0] === 'function') { - domSrc = elems[0].call(el, i, html(el.children)); - } else { - domSrc = elems; - } + var domSrc = + typeof elems[0] === 'function' + ? elems[0].call(el, i, html(el.children)) + : elems; - dom = this._makeDomArray(domSrc, i < lastIdx); + var dom = this._makeDomArray(domSrc, i < lastIdx); // Add element before `el` element uniqueSplice(siblings, index, 0, dom, parent); }); - - return this; }; /** @@ -592,10 +571,8 @@ exports.insertBefore = function (target) { * @see {@link http://api.jquery.com/remove/} */ exports.remove = function (selector) { - var elems = this; - // Filter if we have selector - if (selector) elems = elems.filter(selector); + var elems = selector ? this.filter(selector) : this; domEach(elems, function (i, el) { DomUtils.removeElement(el); @@ -624,25 +601,22 @@ exports.remove = function (selector) { * @see {@link http://api.jquery.com/replaceWith/} */ exports.replaceWith = function (content) { - var self = this; - - domEach(this, function (i, el) { + return domEach(this, function (i, el) { var parent = el.parent; if (!parent) { return; } var siblings = parent.children; - var dom = self._makeDomArray( + var dom = this._makeDomArray( typeof content === 'function' ? content.call(el, i, el) : content ); - var index; // In the case that `dom` contains nodes that already exist in other // structures, ensure those nodes are properly removed. updateDOM(dom, null); - index = siblings.indexOf(el); + var index = siblings.indexOf(el); // Completely remove old element uniqueSplice(siblings, index, 1, dom, parent); @@ -651,8 +625,6 @@ exports.replaceWith = function (content) { el.parent = el.prev = el.next = null; } }); - - return this; }; /** @@ -667,14 +639,13 @@ exports.replaceWith = function (content) { * @see {@link http://api.jquery.com/empty/} */ exports.empty = function () { - domEach(this, function (i, el) { + return domEach(this, function (i, el) { el.children.forEach(function (child) { child.next = child.prev = child.parent = null; }); el.children.length = 0; }); - return this; }; /** @@ -702,7 +673,7 @@ exports.html = function (str) { var opts = this.options; - domEach(this, function (i, el) { + return domEach(this, function (i, el) { el.children.forEach(function (child) { child.next = child.prev = child.parent = null; }); @@ -713,8 +684,6 @@ exports.html = function (str) { updateDOM(content, el); }); - - return this; }; exports.toString = function () { @@ -746,14 +715,13 @@ exports.text = function (str) { return text(this); } else if (typeof str === 'function') { // Function support - var self = this; return domEach(this, function (i, el) { - return exports.text.call(self._make(el), str.call(el, i, text([el]))); + return exports.text.call(this._make(el), str.call(el, i, text([el]))); }); } // Append text node to each selected elements - domEach(this, function (i, el) { + return domEach(this, function (i, el) { el.children.forEach(function (child) { child.next = child.prev = child.parent = null; }); @@ -762,8 +730,6 @@ exports.text = function (str) { updateDOM(textNode, el); }); - - return this; }; /** diff --git a/lib/api/traversing.js b/lib/api/traversing.js index 1b159d280b..e6933bacf8 100644 --- a/lib/api/traversing.js +++ b/lib/api/traversing.js @@ -27,34 +27,27 @@ var reSiblingSelector = /^\s*[~+]/; * @see {@link http://api.jquery.com/find/} */ exports.find = function (selectorOrHaystack) { - var contains = this.constructor.contains; - var haystack; - - if (selectorOrHaystack && typeof selectorOrHaystack !== 'string') { - if (selectorOrHaystack.cheerio) { - haystack = selectorOrHaystack.get(); - } else { - haystack = [selectorOrHaystack]; - } + if (!selectorOrHaystack) { + return this._make([]); + } + + var context = this.toArray(); + + if (typeof selectorOrHaystack !== 'string') { + var contains = this.constructor.contains; + var haystack = selectorOrHaystack.cheerio + ? selectorOrHaystack.get() + : [selectorOrHaystack]; return this._make( haystack.filter(function (elem) { - var idx; - var len; - for (idx = 0, len = this.length; idx < len; ++idx) { - if (contains(this[idx], elem)) { - return true; - } - } - }, this) + return context.some(function (node) { + return contains(node, elem); + }); + }) ); } - if (!selectorOrHaystack) { - return this._make([]); - } - - var context = this.toArray(); var elems = reSiblingSelector.test(selectorOrHaystack) ? context : context.reduce(function (newElems, elem) { @@ -253,7 +246,7 @@ exports.next = function (selector) { } var elems = []; - this.toArray().forEach(function (elem) { + domEach(this, function (idx, elem) { while ((elem = elem.next)) { if (isTag(elem)) { elems.push(elem); @@ -288,7 +281,7 @@ exports.nextAll = function (selector) { } var elems = []; - this.toArray().forEach(function (elem) { + domEach(this, function (idx, elem) { while ((elem = elem.next)) { if (isTag(elem) && elems.indexOf(elem) === -1) { elems.push(elem); @@ -331,7 +324,7 @@ exports.nextUntil = function (selector, filterSelector) { untilNode = selector; } - this.toArray().forEach(function (elem) { + domEach(this, function (idx, elem) { while ((elem = elem.next)) { if ( (untilNode && elem !== untilNode) || @@ -371,7 +364,7 @@ exports.prev = function (selector) { } var elems = []; - this.toArray().forEach(function (elem) { + domEach(this, function (idx, elem) { while ((elem = elem.prev)) { if (isTag(elem)) { elems.push(elem); @@ -406,7 +399,7 @@ exports.prevAll = function (selector) { } var elems = []; - this.toArray().forEach(function (elem) { + domEach(this, function (idx, elem) { while ((elem = elem.prev)) { if (isTag(elem) && elems.indexOf(elem) === -1) { elems.push(elem); @@ -449,7 +442,7 @@ exports.prevUntil = function (selector, filterSelector) { untilNode = selector; } - this.toArray().forEach(function (elem) { + domEach(this, function (idx, elem) { while ((elem = elem.prev)) { if ( (untilNode && elem !== untilNode) || @@ -690,16 +683,14 @@ exports.filter = function (match, container) { 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)); + var matches = new Set(select.filter(match, elements, this.options)); elements = elements.filter(function (el) { return !matches.has(el); }); } else { - filterFn = getFilterFn(match); + var filterFn = getFilterFn(match); elements = elements.filter(function (el, i) { return !filterFn(el, i); }); @@ -729,8 +720,8 @@ exports.not = function (match, container) { */ exports.has = function (selectorOrHaystack) { var that = this; - return exports.filter.call(this, function () { - return that._make(this).find(selectorOrHaystack).length > 0; + return exports.filter.call(this, function (i, el) { + return that._make(el).find(selectorOrHaystack).length > 0; }); }; diff --git a/lib/cheerio.js b/lib/cheerio.js index 8e69a9b8ee..e84f59dd35 100644 --- a/lib/cheerio.js +++ b/lib/cheerio.js @@ -55,6 +55,11 @@ var Cheerio = (module.exports = function (selector, context, root, options) { this._root = Cheerio.call(this, root); } + // $() + if (typeof selector === 'string' && isHtml(selector)) { + selector = parse(selector, this.options, false).children; + } + // $($) if (selector.cheerio) return selector; @@ -70,11 +75,6 @@ var Cheerio = (module.exports = function (selector, context, root, options) { return this; } - // $() - if (typeof selector === 'string' && isHtml(selector)) { - return Cheerio.call(this, parse(selector, this.options, false).children); - } - // If we don't have a context, maybe we have a root, from loading if (!context) { context = this._root; @@ -85,7 +85,7 @@ var Cheerio = (module.exports = function (selector, context, root, options) { context = Cheerio.call(this, context); } else { // $('li', 'ul') - selector = [context, selector].join(' '); + selector = context + ' ' + selector; context = this._root; } } else if (!context.cheerio) { @@ -134,20 +134,18 @@ Cheerio.prototype.toArray = function () { }; // Support for (const element of $(...)) iteration: -if (typeof Symbol !== 'undefined') { - Cheerio.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; -} +Cheerio.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; // Plug in the API api.forEach(function (mod) { Object.assign(Cheerio.prototype, mod); }); -var isNode = function (obj) { +function isNode(obj) { return ( obj.name || obj.type === 'root' || obj.type === 'text' || obj.type === 'comment' ); -}; +} diff --git a/lib/parse.js b/lib/parse.js index 51ca038089..f909fd91cb 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -4,46 +4,39 @@ var htmlparser = require('htmlparser2'); var parse5 = require('parse5'); var htmlparser2Adapter = require('parse5-htmlparser2-tree-adapter'); -var domhandler = require('domhandler'); var DomUtils = htmlparser.DomUtils; +var Document = require('domhandler').Document; /* Parser */ exports = module.exports = function parse(content, options, isDocument) { - // options = options || $.fn.options; - - var dom; - if (typeof Buffer !== 'undefined' && Buffer.isBuffer(content)) { content = content.toString(); } if (typeof content === 'string') { - var useHtmlParser2 = options.xmlMode || options._useHtmlParser2; - - dom = useHtmlParser2 + return options.xmlMode || options._useHtmlParser2 ? htmlparser.parseDocument(content, options) : parseWithParse5(content, options, isDocument); - } else { - if ( - typeof content === 'object' && - content != null && - content.type === 'root' - ) { - dom = content; - } else { - // Generic root element - var root = new domhandler.Document(content); - - // Update the DOM using the root - exports.update(content, root); + } - dom = root; - } + if ( + typeof content === 'object' && + content != null && + content.type === 'root' + ) { + // If `content` is already a root, just return it + return content; } - return dom; + // Add conent to new root element + var root = new Document(content); + + // Update the DOM using the root + exports.update(content, root); + + return root; }; function parseWithParse5(content, options, isDocument) { diff --git a/lib/static.js b/lib/static.js index 1a8b40a7f2..2cf285ebb5 100644 --- a/lib/static.js +++ b/lib/static.js @@ -38,13 +38,13 @@ exports.load = function (content, options, isDocument) { var root = parse(content, options, isDocument); - var initialize = function (selector, context, r, opts) { + function initialize(selector, context, r, opts) { if (!(this instanceof initialize)) { return new initialize(selector, context, r, opts); } opts = Object.assign({}, options, opts); return Cheerio.call(this, selector, context, r || root, opts); - }; + } // Ensure that selections created by the "loaded" `initialize` function are // true Cheerio instances. @@ -125,8 +125,8 @@ exports.html = function (dom, options) { dom = undefined; } - // sometimes $.html() used without preloading html - // so fallback non existing options to the default ones + // Sometimes `$.html()` is used without preloading html, + // so fallback non-existing options to the default ones. options = Object.assign( {}, defaultOptions, @@ -151,7 +151,7 @@ exports.xml = function (dom) { /** * Render the document as text. * - * @param {string|cheerio|node} [elems] - Elements to render. + * @param {cheerio|node} [elems] - Elements to render. */ exports.text = function (elems) { if (!elems) { @@ -160,10 +160,9 @@ exports.text = function (elems) { var ret = ''; var len = elems.length; - var elem; for (var i = 0; i < len; i++) { - elem = elems[i]; + var elem = elems[i]; if (elem.type === 'text') ret += elem.data; else if ( elem.children && @@ -190,8 +189,6 @@ exports.text = function (elems) { * @see {@link https://api.jquery.com/jQuery.parseHTML/} */ exports.parseHTML = function (data, context, keepScripts) { - var parsed; - if (!data || typeof data !== 'string') { return null; } @@ -200,7 +197,7 @@ exports.parseHTML = function (data, context, keepScripts) { keepScripts = context; } - parsed = this.load(data, defaultOptions, false); + var parsed = this.load(data, defaultOptions, false); if (!keepScripts) { parsed('script').remove(); } diff --git a/test/api/deprecated.js b/test/api/deprecated.js index dce84dfcbd..b1ec50a635 100644 --- a/test/api/deprecated.js +++ b/test/api/deprecated.js @@ -210,16 +210,16 @@ describe('deprecated APIs', function () { * is deprecated. * * In order to promote consistency with the jQuery library, users are - * encouraged to instead use the instance method of the same name. For - * example: + * encouraged to instead use the instance method of the same name. * + * @example * var $ = cheerio.load('

Hello, world.

'); * $('h1').html(); // '

Hello, world.'. * - * To render the markup of an entire document, invoke the `html` function - * exported by the Cheerio module with a "root" selection, e.g. + * @example To render the markup of an entire document, invoke the `html` function + * exported by the Cheerio module with a "root" selection. * - * Cheerio.html($.root()); // '

Hello, world.

'. + * cheerio.html($.root()); // '

Hello, world.

'. */ describe('.html - deprecated API', function () { it('() : of empty cheerio object should return null', function () { diff --git a/test/api/manipulation.js b/test/api/manipulation.js index 2bf629635b..ca239723b0 100644 --- a/test/api/manipulation.js +++ b/test/api/manipulation.js @@ -73,11 +73,9 @@ describe('$(...)', function () { }); it('(selector) : wraps the content with a copy of the first matched element', function () { - var $oranges; - $('.apple').wrap('.orange, .pear'); - $oranges = $('.orange'); + var $oranges = $('.orange'); expect($('.pear')).to.have.length(1); expect($oranges).to.have.length(2); expect($oranges.eq(0).parent()[0]).to.be($fruits[0]); @@ -209,9 +207,8 @@ describe('$(...)', function () { }); it("(selector) : should wrap the html of the element with the selector's first match", function () { - var $oranges; $('.apple').wrapInner('.orange, .pear'); - $oranges = $('.orange'); + var $oranges = $('.orange'); expect($('.pear')).to.have.length(1); expect($oranges).to.have.length(2); expect($oranges.eq(0).parent()[0]).to.be($('.apple')[0]); @@ -345,11 +342,10 @@ describe('$(...)', function () { it('(existing Node) : should remove node from previous location', function () { var apple = $fruits.children()[0]; - var $children; expect($fruits.children()).to.have.length(3); $fruits.append(apple); - $children = $fruits.children(); + var $children = $fruits.children(); expect($children).to.have.length(3); expect($children[0]).to.not.equal(apple); @@ -358,12 +354,11 @@ describe('$(...)', function () { it('(existing Node) : should remove existing node from previous location', function () { var apple = $fruits.children()[0]; - var $children; var $dest = $('
'); expect($fruits.children()).to.have.length(3); $dest.append(apple); - $children = $fruits.children(); + var $children = $fruits.children(); expect($children).to.have.length(2); expect($children[0]).to.not.equal(apple); @@ -380,11 +375,10 @@ describe('$(...)', function () { it('(existing Node) : should clone all but the last occurrence', function () { var $originalApple = $('.apple'); - var $apples; $('.orange, .pear').append($originalApple); - $apples = $('.apple'); + var $apples = $('.apple'); expect($apples).to.have.length(2); expect($apples.eq(0).parent()[0]).to.be($('.orange')[0]); expect($apples.eq(1).parent()[0]).to.be($('.pear')[0]); @@ -436,17 +430,14 @@ describe('$(...)', function () { it('(fn) : should add returned string as last child', function () { $fruits = $fruits.children(); - var $apple; - var $orange; - var $pear; $fruits.append(function () { return '
'; }); - $apple = $fruits.eq(0); - $orange = $fruits.eq(1); - $pear = $fruits.eq(2); + var $apple = $fruits.eq(0); + var $orange = $fruits.eq(1); + var $pear = $fruits.eq(2); expect($apple.find('.first')[0]).to.equal($apple.contents()[1]); expect($orange.find('.first')[0]).to.equal($orange.contents()[1]); @@ -454,18 +445,15 @@ describe('$(...)', function () { }); it('(fn) : should add returned Cheerio object as last child', function () { - var $apple; - var $orange; - var $pear; $fruits = $fruits.children(); $fruits.append(function () { return $('
'); }); - $apple = $fruits.eq(0); - $orange = $fruits.eq(1); - $pear = $fruits.eq(2); + var $apple = $fruits.eq(0); + var $orange = $fruits.eq(1); + var $pear = $fruits.eq(2); expect($apple.find('.second')[0]).to.equal($apple.contents()[1]); expect($orange.find('.second')[0]).to.equal($orange.contents()[1]); @@ -473,18 +461,15 @@ describe('$(...)', function () { }); it('(fn) : should add returned Node as last child', function () { - var $apple; - var $orange; - var $pear; $fruits = $fruits.children(); $fruits.append(function () { return $('
')[0]; }); - $apple = $fruits.eq(0); - $orange = $fruits.eq(1); - $pear = $fruits.eq(2); + var $apple = $fruits.eq(0); + var $orange = $fruits.eq(1); + var $pear = $fruits.eq(2); expect($apple.find('.third')[0]).to.equal($apple.contents()[1]); expect($orange.find('.third')[0]).to.equal($orange.contents()[1]); @@ -551,11 +536,10 @@ describe('$(...)', function () { it('(existing Node) : should remove existing nodes from previous locations', function () { var pear = $fruits.children()[2]; - var $children; expect($fruits.children()).to.have.length(3); $fruits.prepend(pear); - $children = $fruits.children(); + var $children = $fruits.children(); expect($children).to.have.length(3); expect($children[2]).to.not.equal(pear); @@ -570,11 +554,10 @@ describe('$(...)', function () { it('(existing Node) : should clone all but the last occurrence', function () { var $originalApple = $('.apple'); - var $apples; $('.orange, .pear').prepend($originalApple); - $apples = $('.apple'); + var $apples = $('.apple'); expect($apples).to.have.length(2); expect($apples.eq(0).parent()[0]).to.be($('.orange')[0]); expect($apples.eq(1).parent()[0]).to.be($('.pear')[0]); @@ -625,18 +608,15 @@ describe('$(...)', function () { }); it('(fn) : should add returned string as first child', function () { - var $apple; - var $orange; - var $pear; $fruits = $fruits.children(); $fruits.prepend(function () { return '
'; }); - $apple = $fruits.eq(0); - $orange = $fruits.eq(1); - $pear = $fruits.eq(2); + var $apple = $fruits.eq(0); + var $orange = $fruits.eq(1); + var $pear = $fruits.eq(2); expect($apple.find('.first')[0]).to.equal($apple.contents()[0]); expect($orange.find('.first')[0]).to.equal($orange.contents()[0]); @@ -644,18 +624,15 @@ describe('$(...)', function () { }); it('(fn) : should add returned Cheerio object as first child', function () { - var $apple; - var $orange; - var $pear; $fruits = $fruits.children(); $fruits.prepend(function () { return $('
'); }); - $apple = $fruits.eq(0); - $orange = $fruits.eq(1); - $pear = $fruits.eq(2); + var $apple = $fruits.eq(0); + var $orange = $fruits.eq(1); + var $pear = $fruits.eq(2); expect($apple.find('.second')[0]).to.equal($apple.contents()[0]); expect($orange.find('.second')[0]).to.equal($orange.contents()[0]); @@ -663,18 +640,15 @@ describe('$(...)', function () { }); it('(fn) : should add returned Node as first child', function () { - var $apple; - var $orange; - var $pear; $fruits = $fruits.children(); $fruits.prepend(function () { return $('
')[0]; }); - $apple = $fruits.eq(0); - $orange = $fruits.eq(1); - $pear = $fruits.eq(2); + var $apple = $fruits.eq(0); + var $orange = $fruits.eq(1); + var $pear = $fruits.eq(2); expect($apple.find('.third')[0]).to.equal($apple.contents()[0]); expect($orange.find('.third')[0]).to.equal($orange.contents()[0]); @@ -784,11 +758,10 @@ describe('$(...)', function () { it('(existing Node) : should remove existing nodes from previous locations', function () { var pear = $fruits.children()[2]; - var $children; $('.apple').after(pear); - $children = $fruits.children(); + var $children = $fruits.children(); expect($children).to.have.length(3); expect($children[1]).to.be(pear); }); @@ -1052,11 +1025,10 @@ describe('$(...)', function () { it('(existing Node) : should remove existing nodes from previous locations', function () { var pear = $fruits.children()[2]; - var $children; $('.apple').before(pear); - $children = $fruits.children(); + var $children = $fruits.children(); expect($children).to.have.length(3); expect($children[0]).to.be(pear); }); @@ -1392,13 +1364,13 @@ describe('$(...)', function () { }); it('(self) : should be replaced after replacing it with itself', function () { - var $ = cheerio.load('foo', null, false); + var $a = cheerio.load('foo', null, false); var replacement = 'bar'; - $('a').replaceWith(function (i, el) { + $a('a').replaceWith(function (i, el) { return el; }); - $('a').replaceWith(replacement); - expect($.html()).to.be.equal(replacement); + $a('a').replaceWith(replacement); + expect($a.html()).to.be.equal(replacement); }); it('(str) : should accept strings', function () { @@ -1618,10 +1590,9 @@ describe('$(...)', function () { it('(text) : should create a Node with the DOM level 1 API', function () { var $apple = $('.apple'); - var textNode; $apple.text('anything'); - textNode = $apple[0].childNodes[0]; + var textNode = $apple[0].childNodes[0]; expect(textNode.parentNode).to.be($apple[0]); expect(textNode.nodeType).to.be(3); diff --git a/test/api/utils.js b/test/api/utils.js index 8732f8a54c..3893268885 100644 --- a/test/api/utils.js +++ b/test/api/utils.js @@ -230,10 +230,10 @@ describe('cheerio', function () { }); it('(text) : should return an array that is not effected by DOM manipulation methods', function () { - var $ = cheerio.load('
'); - var elems = $.parseHTML(''); + var $div = cheerio.load('
'); + var elems = $div.parseHTML(''); - $('div').append(elems); + $div('div').append(elems); expect(elems).to.have.length(2); }); diff --git a/test/cheerio.js b/test/cheerio.js index 865296b4c8..7217ea8fd4 100644 --- a/test/cheerio.js +++ b/test/cheerio.js @@ -54,7 +54,7 @@ describe('cheerio', function () { expect($script[0].childNodes).to.be.empty(); }); - var testAppleSelect = function ($apple) { + function testAppleSelect($apple) { expect($apple).to.have.length(1); $apple = $apple[0]; expect($apple.parentNode.tagName).to.equal('ul'); @@ -62,7 +62,7 @@ describe('cheerio', function () { expect($apple.next.attribs['class']).to.equal('orange'); expect($apple.childNodes).to.have.length(1); expect($apple.childNodes[0].data).to.equal('Apple'); - }; + } it('should be able to select .apple with only a context', function () { var $apple = cheerio('.apple', fruits); @@ -323,7 +323,6 @@ describe('cheerio', function () { describe('prototype extensions', function () { it('should honor extensions defined on `prototype` property', function () { var $ = cheerio.load('
'); - var $div; $.prototype.myPlugin = function () { return { context: this, @@ -331,7 +330,7 @@ describe('cheerio', function () { }; }; - $div = $('div'); + var $div = $('div'); expect($div.myPlugin).to.be.a('function'); expect($div.myPlugin().context).to.be($div); @@ -344,7 +343,6 @@ describe('cheerio', function () { it('should honor extensions defined on `fn` property', function () { var $ = cheerio.load('
'); - var $div; $.fn.myPlugin = function () { return { context: this, @@ -352,7 +350,7 @@ describe('cheerio', function () { }; }; - $div = $('div'); + var $div = $('div'); expect($div.myPlugin).to.be.a('function'); expect($div.myPlugin().context).to.be($div); diff --git a/test/xml.js b/test/xml.js index e26ea8fb3f..c9d7ddd089 100644 --- a/test/xml.js +++ b/test/xml.js @@ -1,16 +1,16 @@ var expect = require('expect.js'); var cheerio = require('..'); -var xml = function (str, options) { +function xml(str, options) { options = Object.assign({ xml: true }, options); var $ = cheerio.load(str, options); return $.xml(); -}; +} -var dom = function (str, options) { +function dom(str, options) { var $ = cheerio.load('', options); return $(str).html(); -}; +} describe('render', function () { describe('(xml)', function () { diff --git a/types/index.d.ts b/types/index.d.ts index 77ac1c2c08..e55f5ab8f7 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -267,7 +267,7 @@ declare namespace cheerio { version: string; load( html: string | { toString(): string }, - options?: CheerioParserOptions|null, + options?: CheerioParserOptions | null, isDocument?: boolean ): Root; load(element: Element, options?: CheerioParserOptions): Root;