-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: Move initialization logic to
load
(#1951)
- Loading branch information
Showing
2 changed files
with
121 additions
and
110 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ import { | |
} from './options'; | ||
import * as staticMethods from './static'; | ||
import { Cheerio } from './cheerio'; | ||
import { isHtml, isCheerio } from './utils'; | ||
import parse from './parse'; | ||
import type { Node, Document, Element } from 'domhandler'; | ||
import type * as Load from './load'; | ||
|
@@ -94,30 +95,113 @@ export function load( | |
} | ||
|
||
const internalOpts = { ...defaultOptions, ...flattenOptions(options) }; | ||
const root = parse(content, internalOpts, isDocument); | ||
const initialRoot = parse(content, internalOpts, isDocument); | ||
|
||
/** Create an extended class here, so that extensions only live on one instance. */ | ||
class LoadedCheerio<T> extends Cheerio<T> {} | ||
|
||
function initialize<T>( | ||
selector?: T extends Node | ||
? string | Cheerio<T> | T[] | T | ||
: Cheerio<T> | T[], | ||
context?: string | Cheerio<Node> | Node[] | Node, | ||
r: string | Cheerio<Document> | Document | null = root, | ||
class LoadedCheerio<T> extends Cheerio<T> { | ||
_make<T>( | ||
selector?: ArrayLike<T> | T | string, | ||
context?: BasicAcceptedElems<Node> | null | ||
): Cheerio<T> { | ||
const cheerio = initialize(selector, context); | ||
cheerio.prevObject = this; | ||
|
||
return cheerio; | ||
} | ||
} | ||
|
||
function initialize<T = Node, S extends string = string>( | ||
selector?: ArrayLike<T> | T | S, | ||
context?: BasicAcceptedElems<Node> | null, | ||
root: BasicAcceptedElems<Document> = initialRoot, | ||
opts?: CheerioOptions | ||
) { | ||
return new LoadedCheerio<T>(selector, context, r, { | ||
): Cheerio<S extends SelectorType ? Element : T> { | ||
type Result = S extends SelectorType ? Element : T; | ||
|
||
// $($) | ||
if (selector && isCheerio<Result>(selector)) return selector; | ||
|
||
const options = { | ||
...internalOpts, | ||
...flattenOptions(opts), | ||
}); | ||
}; | ||
const r = | ||
typeof root === 'string' | ||
? [parse(root, options, false)] | ||
: 'length' in root | ||
? root | ||
: [root]; | ||
const rootInstance = isCheerio<Document>(r) | ||
? r | ||
: new LoadedCheerio<Document>(r, null, options); | ||
// Add a cyclic reference, so that calling methods on `_root` never fails. | ||
rootInstance._root = rootInstance; | ||
|
||
// $(), $(null), $(undefined), $(false) | ||
if (!selector) { | ||
return new LoadedCheerio<Result>(undefined, rootInstance, options); | ||
} | ||
|
||
const elements: Node[] | undefined = | ||
typeof selector === 'string' && isHtml(selector) | ||
? // $(<html>) | ||
parse(selector, options, false).children | ||
: isNode(selector) | ||
? // $(dom) | ||
[selector] | ||
: Array.isArray(selector) | ||
? // $([dom]) | ||
selector | ||
: undefined; | ||
|
||
const instance = new LoadedCheerio(elements, rootInstance, options); | ||
|
||
if (elements || !selector) { | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
fb55
Author
Member
|
||
return instance as any; | ||
} | ||
|
||
if (typeof selector !== 'string') throw new Error(''); | ||
|
||
// We know that our selector is a string now. | ||
let search = selector; | ||
|
||
const searchContext: Cheerio<Node> | undefined = !context | ||
? // If we don't have a context, maybe we have a root, from loading | ||
rootInstance | ||
: typeof context === 'string' | ||
? isHtml(context) | ||
? // $('li', '<ul>...</ul>') | ||
new LoadedCheerio<Document>( | ||
[parse(context, options, false)], | ||
rootInstance, | ||
options | ||
) | ||
: // $('li', 'ul') | ||
((search = `${context} ${search}` as S), rootInstance) | ||
: isCheerio<Node>(context) | ||
? // $('li', $) | ||
context | ||
: // $('li', node), $('li', [nodes]) | ||
new LoadedCheerio<Node>( | ||
Array.isArray(context) ? context : [context], | ||
rootInstance, | ||
options | ||
); | ||
|
||
// If we still don't have a context, return | ||
if (!searchContext) return instance as any; | ||
|
||
/* | ||
* #id, .class, tag | ||
*/ | ||
return searchContext.find(search) as Cheerio<Result>; | ||
} | ||
|
||
// Add in static methods & properties | ||
Object.assign(initialize, staticMethods, { | ||
load, | ||
// `_root` and `_options` are used in static methods. | ||
_root: root, | ||
_root: initialRoot, | ||
_options: internalOpts, | ||
// Add `fn` for plugins | ||
fn: LoadedCheerio.prototype, | ||
|
@@ -127,3 +211,12 @@ export function load( | |
|
||
return initialize as CheerioAPI; | ||
} | ||
|
||
function isNode(obj: any): obj is Node { | ||
return ( | ||
!!obj.name || | ||
obj.type === 'root' || | ||
obj.type === 'text' || | ||
obj.type === 'comment' | ||
); | ||
} |
!selector
seems to be moot here since it's already checked on line 141:https://lgtm.com/projects/g/cheeriojs/cheerio/?mode=list