Skip to content

Commit c370f4e

Browse files
fb555saviahv
andauthoredMay 17, 2021
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 <49443574+5saviahv@users.noreply.github.com>
1 parent b59b6b1 commit c370f4e

14 files changed

+209
-214
lines changed
 

‎Readme.md

+1-1
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

+3-2
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

+1-1
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

+3-4
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

+1-1
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

+4-8
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

+10-2
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

+18-2
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

+9-5
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);

‎src/cheerio.ts

+30-104
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,8 @@
11
import parse from './parse';
2-
import {
3-
CheerioOptions,
4-
InternalOptions,
5-
default as defaultOptions,
6-
flatten as flattenOptions,
7-
} from './options';
2+
import { InternalOptions, default as defaultOptions } from './options';
83
import { isHtml, isCheerio } from './utils';
9-
import type { Node, Document, Element } from 'domhandler';
10-
import * as Static from './static';
11-
import type { load } from './load';
12-
import { SelectorType, BasicAcceptedElems } from './types';
4+
import type { Node, Document } from 'domhandler';
5+
import { BasicAcceptedElems } from './types';
136

147
import * as Attributes from './api/attributes';
158
import * as Traversing from './api/traversing';
@@ -23,16 +16,11 @@ type ManipulationType = typeof Manipulation;
2316
type CssType = typeof Css;
2417
type FormsType = typeof Forms;
2518

26-
/*
27-
* The API
28-
*/
29-
const api = [Attributes, Traversing, Manipulation, Css, Forms];
30-
3119
export class Cheerio<T> implements ArrayLike<T> {
32-
length!: number;
20+
length = 0;
3321
[index: number]: T;
3422

35-
options!: InternalOptions;
23+
options: InternalOptions;
3624
/**
3725
* The root of the document. Can be overwritten by using the `root` argument
3826
* of the constructor.
@@ -42,37 +30,6 @@ export class Cheerio<T> implements ArrayLike<T> {
4230
_root: Cheerio<Document> | undefined;
4331
/** @function */
4432
find!: typeof Traversing.find;
45-
/**
46-
* The root the document was originally loaded with. Same as the static
47-
* `_root` property.
48-
*
49-
* @private
50-
*/
51-
_originalRoot: Document | undefined;
52-
53-
/**
54-
* The root the document was originally loaded with. Set in `.load`.
55-
*
56-
* @private
57-
*/
58-
static _root: Document | undefined;
59-
/**
60-
* The options the document was originally loaded with. Set in `.load`.
61-
*
62-
* @private
63-
*/
64-
static _options: InternalOptions | undefined;
65-
public static html = Static.html;
66-
public static xml = Static.xml;
67-
public static text = Static.text;
68-
public static parseHTML = Static.parseHTML;
69-
public static root = Static.root;
70-
public static contains = Static.contains;
71-
public static merge = Static.merge;
72-
public static load: typeof load;
73-
74-
/** Mimic jQuery's prototype alias for plugin authors. */
75-
public static fn = Cheerio.prototype;
7633

7734
/**
7835
* Instance of cheerio. Methods are specified in the modules. Usage of this
@@ -87,27 +44,24 @@ export class Cheerio<T> implements ArrayLike<T> {
8744
constructor(
8845
selector?: T extends Node ? BasicAcceptedElems<T> : Cheerio<T> | T[],
8946
context?: BasicAcceptedElems<Node> | null,
90-
root?: BasicAcceptedElems<Document>,
91-
options?: CheerioOptions
47+
root?: BasicAcceptedElems<Document> | null,
48+
options: InternalOptions = defaultOptions
9249
) {
93-
if (!(this instanceof Cheerio)) {
94-
return new Cheerio(selector, context, root, options);
95-
}
96-
97-
this.length = 0;
98-
99-
this.options = {
100-
...defaultOptions,
101-
...this.options,
102-
...flattenOptions(options),
103-
};
50+
this.options = options;
10451

10552
// $(), $(null), $(undefined), $(false)
10653
if (!selector) return this;
10754

10855
if (root) {
10956
if (typeof root === 'string') root = parse(root, this.options, false);
110-
this._root = (Cheerio as any).call(this, root);
57+
this._root = new (this.constructor as typeof Cheerio)(
58+
root,
59+
null,
60+
null,
61+
this.options
62+
);
63+
// Add a cyclic reference, so that calling methods on `_root` never fails.
64+
this._root._root = this._root;
11165
}
11266

11367
// $($)
@@ -142,14 +96,14 @@ export class Cheerio<T> implements ArrayLike<T> {
14296
: typeof context === 'string'
14397
? isHtml(context)
14498
? // $('li', '<ul>...</ul>')
145-
new Cheerio(parse(context, this.options, false))
99+
this._make(parse(context, this.options, false))
146100
: // $('li', 'ul')
147101
((search = `${context} ${search}`), this._root)
148102
: isCheerio(context)
149103
? // $('li', $)
150104
context
151105
: // $('li', node), $('li', [nodes])
152-
new Cheerio(context);
106+
this._make(context);
153107

154108
// If we still don't have a context, return
155109
if (!searchContext) return this;
@@ -172,47 +126,30 @@ export class Cheerio<T> implements ArrayLike<T> {
172126
*/
173127
_make<T>(
174128
dom: Cheerio<T> | T[] | T | string,
175-
context?: BasicAcceptedElems<Node> | null,
176-
root: BasicAcceptedElems<Document> | undefined = this._root
129+
context?: BasicAcceptedElems<Node>
177130
): Cheerio<T> {
178131
const cheerio = new (this.constructor as any)(
179132
dom,
180133
context,
181-
root,
134+
this._root,
182135
this.options
183136
);
184137
cheerio.prevObject = this;
185138

186139
return cheerio;
187140
}
188-
189-
/**
190-
* Retrieve all the DOM elements contained in the jQuery set as an array.
191-
*
192-
* @example
193-
*
194-
* ```js
195-
* $('li').toArray();
196-
* //=> [ {...}, {...}, {...} ]
197-
* ```
198-
*
199-
* @returns The contained items.
200-
*/
201-
toArray(): T[] {
202-
return this.get();
203-
}
204141
}
205142

206143
export interface Cheerio<T>
207144
extends AttributesType,
208145
TraversingType,
209146
ManipulationType,
210147
CssType,
211-
FormsType {
148+
FormsType,
149+
Iterable<T> {
212150
cheerio: '[cheerio object]';
213151

214152
splice: typeof Array.prototype.slice;
215-
[Symbol.iterator](): Iterator<T>;
216153
}
217154

218155
/** Set a signature of the object. */
@@ -227,7 +164,14 @@ Cheerio.prototype.splice = Array.prototype.splice;
227164
Cheerio.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
228165

229166
// Plug in the API
230-
api.forEach((mod) => Object.assign(Cheerio.prototype, mod));
167+
Object.assign(
168+
Cheerio.prototype,
169+
Attributes,
170+
Traversing,
171+
Manipulation,
172+
Css,
173+
Forms
174+
);
231175

232176
function isNode(obj: any): obj is Node {
233177
return (
@@ -237,21 +181,3 @@ function isNode(obj: any): obj is Node {
237181
obj.type === 'comment'
238182
);
239183
}
240-
241-
type CheerioClassType = typeof Cheerio;
242-
243-
/**
244-
* Wrapper around the `Cheerio` class, making it possible to create a new
245-
* instance without using `new`.
246-
*/
247-
export interface CheerioAPI extends CheerioClassType {
248-
<T extends Node, S extends string>(
249-
selector?: S | BasicAcceptedElems<T>,
250-
context?: BasicAcceptedElems<Node> | null,
251-
root?: BasicAcceptedElems<Document>,
252-
options?: CheerioOptions
253-
): Cheerio<S extends SelectorType ? Element : T>;
254-
}
255-
256-
// Make it possible to call Cheerio without using `new`.
257-
export default (Cheerio as unknown) as CheerioAPI;

‎src/index.ts

+9-12
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,9 @@
1-
import Cheerio from './cheerio';
2-
3-
/**
4-
* The default cheerio instance.
5-
*
6-
* @deprecated Use the function returned by `load` instead.
7-
*/
8-
export default Cheerio;
9-
101
/**
112
* The main types of Cheerio objects.
123
*
134
* @category Cheerio
145
*/
15-
export type { Cheerio, CheerioAPI } from './cheerio';
6+
export type { Cheerio } from './cheerio';
167
/**
178
* Types used in signatures of Cheerio methods.
189
*
@@ -33,8 +24,14 @@ export type { Node, NodeWithChildren, Element, Document } from 'domhandler';
3324

3425
export * from './load';
3526
import { load } from './load';
36-
// We add this here, to avoid a cyclic depenency
37-
Cheerio.load = load;
27+
28+
/**
29+
* The default cheerio instance.
30+
*
31+
* @deprecated Use the function returned by `load` instead.
32+
*/
33+
export default load([]);
34+
3835
import * as staticMethods from './static';
3936

4037
/**

‎src/load.ts

+96-41
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,129 @@
11
import {
22
CheerioOptions,
3+
InternalOptions,
34
default as defaultOptions,
45
flatten as flattenOptions,
56
} from './options';
67
import * as staticMethods from './static';
7-
import { CheerioAPI, Cheerio } from './cheerio';
8+
import { Cheerio } from './cheerio';
89
import parse from './parse';
9-
import type { Node, Document } from 'domhandler';
10+
import type { Node, Document, Element } from 'domhandler';
11+
import type * as Load from './load';
12+
import { SelectorType, BasicAcceptedElems } from './types';
13+
14+
type StaticType = typeof staticMethods;
15+
type LoadType = typeof Load;
16+
17+
/**
18+
* A querying function, bound to a document created from the provided markup.
19+
*
20+
* Also provides several helper methods for dealing with the document as a whole.
21+
*/
22+
export interface CheerioAPI extends StaticType, LoadType {
23+
/**
24+
* This selector method is the starting point for traversing and manipulating
25+
* the document. Like jQuery, it's the primary method for selecting elements
26+
* in the document.
27+
*
28+
* `selector` searches within the `context` scope which searches within the
29+
* `root` scope.
30+
*
31+
* @example
32+
*
33+
* ```js
34+
* $('.apple', '#fruits').text();
35+
* //=> Apple
36+
*
37+
* $('ul .pear').attr('class');
38+
* //=> pear
39+
*
40+
* $('li[class=orange]').html();
41+
* //=> Orange
42+
* ```
43+
*
44+
* @param selector - Either a selector to look for within the document, or the
45+
* contents of a new Cheerio instance.
46+
* @param context - Either a selector to look for within the root, or the
47+
* contents of the document to query.
48+
* @param root - Optional HTML document string.
49+
*/
50+
<T extends Node, S extends string>(
51+
selector?: S | BasicAcceptedElems<T>,
52+
context?: BasicAcceptedElems<Node> | null,
53+
root?: BasicAcceptedElems<Document>,
54+
options?: CheerioOptions
55+
): Cheerio<S extends SelectorType ? Element : T>;
56+
57+
/**
58+
* The root the document was originally loaded with.
59+
*
60+
* @private
61+
*/
62+
_root: Document;
63+
64+
/**
65+
* The options the document was originally loaded with.
66+
*
67+
* @private
68+
*/
69+
_options: InternalOptions;
70+
71+
/** Mimic jQuery's prototype alias for plugin authors. */
72+
fn: typeof Cheerio.prototype;
73+
}
1074

1175
/**
1276
* Create a querying function, bound to a document created from the provided
1377
* markup. Note that similar to web browser contexts, this operation may
1478
* introduce `<html>`, `<head>`, and `<body>` elements; set `isDocument` to
1579
* `false` to switch to fragment mode and disable this.
1680
*
17-
* See the README section titled "Loading" for additional usage information.
18-
*
1981
* @param content - Markup to be loaded.
2082
* @param options - Options for the created instance.
2183
* @param isDocument - Allows parser to be switched to fragment mode.
2284
* @returns The loaded document.
85+
* @see {@link https://cheerio.js.org#loading} for additional usage information.
2386
*/
2487
export function load(
2588
content: string | Node | Node[] | Buffer,
2689
options?: CheerioOptions | null,
27-
isDocument?: boolean
90+
isDocument = true
2891
): CheerioAPI {
2992
if ((content as string | null) == null) {
3093
throw new Error('cheerio.load() expects a string');
3194
}
3295

33-
options = { ...defaultOptions, ...flattenOptions(options) };
34-
35-
if (typeof isDocument === 'undefined') isDocument = true;
96+
const internalOpts = { ...defaultOptions, ...flattenOptions(options) };
97+
const root = parse(content, internalOpts, isDocument);
3698

37-
const root = parse(content, options, isDocument);
99+
/** Create an extended class here, so that extensions only live on one instance. */
100+
class LoadedCheerio<T> extends Cheerio<T> {}
38101

39-
class initialize<T> extends Cheerio<T> {
40-
// Mimic jQuery's prototype alias for plugin authors.
41-
static fn = initialize.prototype;
42-
43-
constructor(
44-
selector?: T extends Node
45-
? string | Cheerio<T> | T[] | T
46-
: Cheerio<T> | T[],
47-
context?: string | Cheerio<Node> | Node[] | Node,
48-
r: string | Cheerio<Document> | Document = root,
49-
opts?: CheerioOptions
50-
) {
51-
// @ts-expect-error Using `this` before calling the constructor.
52-
if (!(this instanceof initialize)) {
53-
return new initialize(selector, context, r, opts);
54-
}
55-
super(selector, context, r, { ...options, ...opts });
56-
}
102+
function initialize<T>(
103+
selector?: T extends Node
104+
? string | Cheerio<T> | T[] | T
105+
: Cheerio<T> | T[],
106+
context?: string | Cheerio<Node> | Node[] | Node,
107+
r: string | Cheerio<Document> | Document | null = root,
108+
opts?: CheerioOptions
109+
) {
110+
return new LoadedCheerio<T>(selector, context, r, {
111+
...internalOpts,
112+
...flattenOptions(opts),
113+
});
57114
}
58115

59-
/*
60-
* Keep a reference to the top-level scope so we can chain methods that implicitly
61-
* resolve selectors; e.g. $("<span>").(".bar"), which otherwise loses ._root
62-
*/
63-
initialize.prototype._originalRoot = root;
64-
65-
// Add in the static methods
66-
Object.assign(initialize, staticMethods, { load });
67-
68-
// Add in the root
69-
initialize._root = root;
70-
// Store options
71-
initialize._options = options;
116+
// Add in static methods & properties
117+
Object.assign(initialize, staticMethods, {
118+
load,
119+
// `_root` and `_options` are used in static methods.
120+
_root: root,
121+
_options: internalOpts,
122+
// Add `fn` for plugins
123+
fn: LoadedCheerio.prototype,
124+
// Add the prototype here to maintain `instanceof` behavior.
125+
prototype: LoadedCheerio.prototype,
126+
});
72127

73-
return (initialize as unknown) as CheerioAPI;
128+
return initialize as CheerioAPI;
74129
}

‎src/static.spec.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import * as fixtures from './__fixtures__/fixtures';
2-
import cheerio from '.';
3-
import { CheerioAPI } from './cheerio';
2+
import cheerio, { CheerioAPI } from '.';
43

54
describe('cheerio', () => {
65
describe('.html', () => {

‎src/static.ts

+23-29
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { CheerioAPI, Cheerio } from './cheerio';
1+
import type { CheerioAPI, Cheerio } from '.';
22
import { Node, Document } from 'domhandler';
33
import {
44
InternalOptions,
@@ -20,24 +20,22 @@ import { render as renderWithHtmlparser2 } from './parsers/htmlparser2';
2020
* @returns The rendered document.
2121
*/
2222
function render(
23-
that: typeof Cheerio | undefined,
23+
that: CheerioAPI | undefined,
2424
dom: ArrayLike<Node> | Node | string | undefined,
2525
options: InternalOptions
2626
): string {
27-
if (!dom) {
28-
if (that?._root?.children) {
29-
dom = that._root.children;
30-
} else {
31-
return '';
32-
}
33-
} else if (typeof dom === 'string') {
34-
dom = select(dom, that?._root ?? [], options);
35-
}
27+
const toRender = dom
28+
? typeof dom === 'string'
29+
? select(dom, that?._root ?? [], options)
30+
: dom
31+
: that?._root.children;
32+
33+
if (!toRender) return '';
3634

3735
return options.xmlMode || options._useHtmlParser2
3836
? // FIXME: Pull in new version of dom-serializer to fix this.
39-
renderWithHtmlparser2(dom as Node[], options)
40-
: renderWithParse5(dom);
37+
renderWithHtmlparser2(toRender as Node[], options)
38+
: renderWithParse5(toRender);
4139
}
4240

4341
/**
@@ -63,10 +61,7 @@ function isOptions(
6361
* @param options - Options for the renderer.
6462
* @returns The rendered document.
6563
*/
66-
export function html(
67-
this: typeof Cheerio | void,
68-
options?: CheerioOptions
69-
): string;
64+
export function html(this: CheerioAPI | void, options?: CheerioOptions): string;
7065
/**
7166
* Renders the document.
7267
*
@@ -75,12 +70,12 @@ export function html(
7570
* @returns The rendered document.
7671
*/
7772
export function html(
78-
this: typeof Cheerio | void,
73+
this: CheerioAPI | void,
7974
dom?: string | ArrayLike<Node> | Node,
8075
options?: CheerioOptions
8176
): string;
8277
export function html(
83-
this: typeof Cheerio | void,
78+
this: CheerioAPI | void,
8479
dom?: string | ArrayLike<Node> | Node | CheerioOptions,
8580
options?: CheerioOptions
8681
): string {
@@ -99,7 +94,7 @@ export function html(
9994
* Sometimes `$.html()` is used without preloading html,
10095
* so fallback non-existing options to the default ones.
10196
*/
102-
options = {
97+
const opts = {
10398
...defaultOptions,
10499
...(this ? this._options : {}),
105100
...flattenOptions(options ?? {}),
@@ -108,7 +103,7 @@ export function html(
108103
return render(
109104
this || undefined,
110105
dom as string | Cheerio<Node> | Node | undefined,
111-
options
106+
opts
112107
);
113108
}
114109

@@ -119,7 +114,7 @@ export function html(
119114
* @returns THe rendered document.
120115
*/
121116
export function xml(
122-
this: typeof Cheerio,
117+
this: CheerioAPI,
123118
dom?: string | ArrayLike<Node> | Node
124119
): string {
125120
const options = { ...this._options, xmlMode: true };
@@ -134,7 +129,7 @@ export function xml(
134129
* @returns The rendered document.
135130
*/
136131
export function text(
137-
this: typeof Cheerio | void,
132+
this: CheerioAPI | void,
138133
elements?: ArrayLike<Node>
139134
): string {
140135
const elems = elements ? elements : this ? this.root() : [];
@@ -170,14 +165,14 @@ export function text(
170165
* @see {@link https://api.jquery.com/jQuery.parseHTML/}
171166
*/
172167
export function parseHTML(
173-
this: typeof Cheerio,
168+
this: CheerioAPI,
174169
data: string,
175170
context?: unknown | boolean,
176171
keepScripts?: boolean
177172
): Node[];
178-
export function parseHTML(this: typeof Cheerio, data?: '' | null): null;
173+
export function parseHTML(this: CheerioAPI, data?: '' | null): null;
179174
export function parseHTML(
180-
this: typeof Cheerio,
175+
this: CheerioAPI,
181176
data?: string | null,
182177
context?: unknown | boolean,
183178
keepScripts = typeof context === 'boolean' ? context : false
@@ -219,9 +214,8 @@ export function parseHTML(
219214
* @returns Cheerio instance wrapping the root node.
220215
* @alias Cheerio.root
221216
*/
222-
export function root(this: typeof Cheerio): Cheerio<Document> {
223-
const fn = (this as unknown) as CheerioAPI;
224-
return fn(this._root);
217+
export function root(this: CheerioAPI): Cheerio<Document> {
218+
return this(this._root);
225219
}
226220

227221
/**

0 commit comments

Comments
 (0)
Please sign in to comment.