Skip to content

Commit c370f4e

Browse files
fb555saviahv
andauthored
refactor: Have load export a function (#1869)
Instead of the constructor. __BREAKING CHANGES__ Low potential for breakages: - Removed the internal `_originalRoot` property. Some methods, such as `appendTo`, can now have their roots overwritten by passing the `root` property to `CheerioAPI`. - The `default` export now has a surrounding `Document`, without any contents. Co-authored-by: 5saviahv <[email protected]>
1 parent b59b6b1 commit c370f4e

14 files changed

+209
-214
lines changed

Readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ Cheerio's selector implementation is nearly identical to jQuery's, so the API is
164164

165165
`selector` searches within the `context` scope which searches within the `root` scope. `selector` and `context` can be a string expression, DOM Element, array of DOM elements, or cheerio object. `root` is typically the HTML document string.
166166

167-
This selector method is the starting point for traversing and manipulating the document. Like jQuery, it's the primary method for selecting elements in the document, but unlike jQuery it's built on top of the CSSSelect library, which implements most of the Sizzle selectors.
167+
This selector method is the starting point for traversing and manipulating the document. Like jQuery, it's the primary method for selecting elements in the document.
168168

169169
```js
170170
$('.apple', '#fruits').text();

src/__tests__/deprecated.spec.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ describe('deprecated APIs', () => {
5858

5959
// #1674 - merge, wont accept Cheerio object
6060
it('should be a able merge array and cheerio object', () => {
61-
const ret = cheerio.merge(new cheerio(), ['elem1', 'elem2'] as any);
61+
const ret = cheerio.merge(cheerio(), ['elem1', 'elem2'] as any);
6262
expect(typeof ret).toBe('object');
6363
expect(ret).toHaveLength(2);
6464
});
@@ -202,7 +202,8 @@ describe('deprecated APIs', () => {
202202
describe('.root', () => {
203203
it('returns an empty selection', () => {
204204
const $empty = cheerio.root();
205-
expect($empty).toHaveLength(0);
205+
expect($empty).toHaveLength(1);
206+
expect($empty[0].children).toHaveLength(0);
206207
});
207208
});
208209
});

src/api/forms.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import cheerio from '../../src';
2-
import type { CheerioAPI } from '../cheerio';
2+
import type { CheerioAPI } from '../load';
33
import { forms } from '../__fixtures__/fixtures';
44

55
describe('$(...)', () => {

src/api/forms.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Node } from 'domhandler';
2-
import type { Cheerio, CheerioAPI } from '../cheerio';
2+
import type { Cheerio } from '../cheerio';
33
import { isTag } from '../utils';
44

55
/*
@@ -54,9 +54,8 @@ export function serializeArray<T extends Node>(
5454
this: Cheerio<T>
5555
): SerializedField[] {
5656
// Resolve all form elements from either forms or collections of form elements
57-
const Cheerio = this.constructor as CheerioAPI;
5857
return this.map((_, elem) => {
59-
const $elem = Cheerio(elem);
58+
const $elem = this._make(elem);
6059
if (isTag(elem) && elem.name === 'form') {
6160
return $elem.find(submittableSelector).toArray();
6261
}
@@ -72,7 +71,7 @@ export function serializeArray<T extends Node>(
7271
// Convert each of the elements to its value(s)
7372
)
7473
.map<Node, SerializedField>((_, elem) => {
75-
const $elem = Cheerio(elem);
74+
const $elem = this._make(elem);
7675
const name = $elem.attr('name') as string; // We have filtered for elements with a name before.
7776
// If there is no value set (e.g. `undefined`, `null`), then default value to empty
7877
const value = $elem.val() ?? '';

src/api/manipulation.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { load } from '../../src';
2-
import type { CheerioAPI, Cheerio } from '../cheerio';
2+
import type { CheerioAPI, Cheerio } from '..';
33
import { fruits, divcontainers, mixedText } from '../__fixtures__/fixtures';
44
import type { Node, Element } from 'domhandler';
55

src/api/manipulation.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,7 @@ export function appendTo<T extends Node>(
168168
this: Cheerio<T>,
169169
target: BasicAcceptedElems<Node>
170170
): Cheerio<T> {
171-
const appendTarget = isCheerio(target)
172-
? target
173-
: this._make(target, null, this._originalRoot);
171+
const appendTarget = isCheerio(target) ? target : this._make(target);
174172

175173
appendTarget.append(this);
176174

@@ -202,9 +200,7 @@ export function prependTo<T extends Node>(
202200
this: Cheerio<T>,
203201
target: BasicAcceptedElems<Node>
204202
): Cheerio<T> {
205-
const prependTarget = isCheerio(target)
206-
? target
207-
: this._make(target, null, this._originalRoot);
203+
const prependTarget = isCheerio(target) ? target : this._make(target);
208204

209205
prependTarget.prepend(this);
210206

@@ -642,7 +638,7 @@ export function insertAfter<T extends Node>(
642638
target: BasicAcceptedElems<Node>
643639
): Cheerio<T> {
644640
if (typeof target === 'string') {
645-
target = this._make<Node>(target, null, this._originalRoot);
641+
target = this._make<Node>(target);
646642
}
647643

648644
this.remove();
@@ -755,7 +751,7 @@ export function insertBefore<T extends Node>(
755751
this: Cheerio<T>,
756752
target: BasicAcceptedElems<Node>
757753
): Cheerio<T> {
758-
const targetArr = this._make<Node>(target, null, this._originalRoot);
754+
const targetArr = this._make<Node>(target);
759755

760756
this.remove();
761757

src/api/traversing.spec.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import cheerio from '../../src';
2-
import type { CheerioAPI, Cheerio } from '../cheerio';
2+
import { Cheerio } from '../cheerio';
3+
import type { CheerioAPI } from '../load';
34
import { Node, Element, isText } from 'domhandler';
45
import {
56
food,
@@ -689,7 +690,7 @@ describe('$(...)', () => {
689690
it('() : should return an empty array', () => {
690691
const result = $('.orange').closest();
691692
expect(result).toHaveLength(0);
692-
expect(result).toBeInstanceOf(cheerio);
693+
expect(result).toBeInstanceOf(Cheerio);
693694
});
694695

695696
it('(selector) : should find the closest element that matches the selector, searching through its ancestors and itself', () => {
@@ -1237,6 +1238,13 @@ describe('$(...)', () => {
12371238
expect($selection[0]).toBe($fruits[0]);
12381239
expect($selection[1]).toBe($orange[0]);
12391240
});
1241+
it('is root object preserved', () => {
1242+
const $selection = $('<div></div>').add('#fruits');
1243+
1244+
expect($selection).toHaveLength(2);
1245+
expect($selection.eq(0).is('div')).toBe(true);
1246+
expect($selection.eq(1).is($fruits.eq(0))).toBe(true);
1247+
});
12401248
});
12411249
describe('(selector) matched elements :', () => {
12421250
it('occur before the current selection', () => {

src/api/traversing.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { Node, Element, hasChildren } from 'domhandler';
88
import type { Cheerio } from '../cheerio';
99
import * as select from 'cheerio-select';
1010
import { domEach, isTag, isCheerio } from '../utils';
11+
import { contains } from '../static';
1112
import { DomUtils } from 'htmlparser2';
1213
import type { FilterFunction, AcceptedFilters } from '../types';
1314
const { uniqueSort } = DomUtils;
@@ -42,7 +43,6 @@ export function find<T extends Node>(
4243
const context: Node[] = this.toArray();
4344

4445
if (typeof selectorOrHaystack !== 'string') {
45-
const { contains } = this.constructor as typeof Cheerio;
4646
const haystack = isCheerio(selectorOrHaystack)
4747
? selectorOrHaystack.get()
4848
: [selectorOrHaystack];
@@ -941,11 +941,27 @@ export function get<T>(this: Cheerio<T>, i: number): T;
941941
export function get<T>(this: Cheerio<T>): T[];
942942
export function get<T>(this: Cheerio<T>, i?: number): T | T[] {
943943
if (i == null) {
944-
return Array.prototype.slice.call(this);
944+
return this.toArray();
945945
}
946946
return this[i < 0 ? this.length + i : i];
947947
}
948948

949+
/**
950+
* Retrieve all the DOM elements contained in the jQuery set as an array.
951+
*
952+
* @example
953+
*
954+
* ```js
955+
* $('li').toArray();
956+
* //=> [ {...}, {...}, {...} ]
957+
* ```
958+
*
959+
* @returns The contained items.
960+
*/
961+
export function toArray<T>(this: Cheerio<T>): T[] {
962+
return Array.prototype.slice.call(this);
963+
}
964+
949965
/**
950966
* Search for a given element from among the matched elements.
951967
*

src/cheerio.spec.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -215,11 +215,6 @@ describe('cheerio', () => {
215215
expect($elem.eq(1).attr('class')).toBe('orange');
216216
});
217217

218-
it('should gracefully degrade on complex, unmatched queries', () => {
219-
const $elem = cheerio('Eastern States Cup #8-fin&nbsp;<1br>Downhill&nbsp;');
220-
expect($elem).toHaveLength(0);
221-
});
222-
223218
it('(extended Array) should not interfere with prototype methods (issue #119)', () => {
224219
const extended: any = [];
225220
extended.find = extended.children = extended.each = function () {
@@ -275,6 +270,15 @@ describe('cheerio', () => {
275270
expect(lis).toHaveLength(3);
276271
});
277272

273+
it('should preserve root content', () => {
274+
const $ = cheerio.load(fruits);
275+
// Root should not be overwritten
276+
const el = $('<div></div>');
277+
expect(Object.is(el, el._root)).toBe(false);
278+
// Query has to have results
279+
expect($('li', 'ul')).toHaveLength(3);
280+
});
281+
278282
it('should allow loading a pre-parsed DOM', () => {
279283
const dom = parseDOM(food);
280284
const $ = cheerio.load(dom);

0 commit comments

Comments
 (0)