|
|
import { |
|
|
type CheerioOptions, |
|
|
type InternalOptions, |
|
|
flattenOptions, |
|
|
} from './options.js'; |
|
|
import * as staticMethods from './static.js'; |
|
|
import { Cheerio } from './cheerio.js'; |
|
|
import { isHtml, isCheerio } from './utils.js'; |
|
|
import type { AnyNode, Document, Element, ParentNode } from 'domhandler'; |
|
|
import type { SelectorType, BasicAcceptedElems } from './types.js'; |
|
|
import { ElementType } from 'htmlparser2'; |
|
|
|
|
|
type StaticType = typeof staticMethods; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export interface CheerioAPI extends StaticType { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<T extends AnyNode, S extends string>( |
|
|
selector?: S | BasicAcceptedElems<T>, |
|
|
context?: BasicAcceptedElems<AnyNode> | null, |
|
|
root?: BasicAcceptedElems<Document>, |
|
|
options?: CheerioOptions, |
|
|
): Cheerio<S extends SelectorType ? Element : T>; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_root: Document; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_options: InternalOptions; |
|
|
|
|
|
|
|
|
fn: typeof Cheerio.prototype; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
load: ReturnType<typeof getLoad>; |
|
|
} |
|
|
|
|
|
export function getLoad( |
|
|
parse: Cheerio<AnyNode>['_parse'], |
|
|
render: ( |
|
|
dom: AnyNode | ArrayLike<AnyNode>, |
|
|
options: InternalOptions, |
|
|
) => string, |
|
|
) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return function load( |
|
|
content: string | AnyNode | AnyNode[] | Buffer, |
|
|
options?: CheerioOptions | null, |
|
|
isDocument = true, |
|
|
): CheerioAPI { |
|
|
if ((content as string | null) == null) { |
|
|
throw new Error('cheerio.load() expects a string'); |
|
|
} |
|
|
|
|
|
const internalOpts = flattenOptions(options); |
|
|
const initialRoot = parse(content, internalOpts, isDocument, null); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class LoadedCheerio<T> extends Cheerio<T> { |
|
|
_make<T>( |
|
|
selector?: ArrayLike<T> | T | string, |
|
|
context?: BasicAcceptedElems<AnyNode> | null, |
|
|
): Cheerio<T> { |
|
|
const cheerio = initialize(selector, context); |
|
|
cheerio.prevObject = this; |
|
|
|
|
|
return cheerio; |
|
|
} |
|
|
|
|
|
_parse( |
|
|
content: string | Document | AnyNode | AnyNode[] | Buffer, |
|
|
options: InternalOptions, |
|
|
isDocument: boolean, |
|
|
context: ParentNode | null, |
|
|
) { |
|
|
return parse(content, options, isDocument, context); |
|
|
} |
|
|
|
|
|
_render(dom: AnyNode | ArrayLike<AnyNode>): string { |
|
|
return render(dom, this.options); |
|
|
} |
|
|
} |
|
|
|
|
|
function initialize<T = AnyNode, S extends string = string>( |
|
|
selector?: ArrayLike<T> | T | S, |
|
|
context?: BasicAcceptedElems<AnyNode> | null, |
|
|
root: BasicAcceptedElems<Document> = initialRoot, |
|
|
opts?: CheerioOptions, |
|
|
): Cheerio<S extends SelectorType ? Element : T> { |
|
|
type Result = S extends SelectorType ? Element : T; |
|
|
|
|
|
|
|
|
if (selector && isCheerio<Result>(selector)) return selector; |
|
|
|
|
|
const options = flattenOptions(opts, internalOpts); |
|
|
const r = |
|
|
typeof root === 'string' |
|
|
? [parse(root, options, false, null)] |
|
|
: 'length' in root |
|
|
? root |
|
|
: [root]; |
|
|
const rootInstance = isCheerio<Document>(r) |
|
|
? r |
|
|
: new LoadedCheerio<Document>(r, null, options); |
|
|
|
|
|
rootInstance._root = rootInstance; |
|
|
|
|
|
|
|
|
if (!selector) { |
|
|
return new LoadedCheerio<Result>(undefined, rootInstance, options); |
|
|
} |
|
|
|
|
|
const elements: AnyNode[] | undefined = |
|
|
typeof selector === 'string' && isHtml(selector) |
|
|
? |
|
|
parse(selector, options, false, null).children |
|
|
: isNode(selector) |
|
|
? |
|
|
[selector] |
|
|
: Array.isArray(selector) |
|
|
? |
|
|
selector |
|
|
: undefined; |
|
|
|
|
|
const instance = new LoadedCheerio(elements, rootInstance, options); |
|
|
|
|
|
if (elements) { |
|
|
return instance as Cheerio<Result>; |
|
|
} |
|
|
|
|
|
if (typeof selector !== 'string') { |
|
|
throw new TypeError('Unexpected type of selector'); |
|
|
} |
|
|
|
|
|
|
|
|
let search = selector; |
|
|
|
|
|
const searchContext: Cheerio<AnyNode> | undefined = context |
|
|
? |
|
|
typeof context === 'string' |
|
|
? isHtml(context) |
|
|
? |
|
|
new LoadedCheerio<Document>( |
|
|
[parse(context, options, false, null)], |
|
|
rootInstance, |
|
|
options, |
|
|
) |
|
|
: |
|
|
((search = `${context} ${search}` as S), rootInstance) |
|
|
: isCheerio<AnyNode>(context) |
|
|
? |
|
|
context |
|
|
: |
|
|
new LoadedCheerio<AnyNode>( |
|
|
Array.isArray(context) ? context : [context], |
|
|
rootInstance, |
|
|
options, |
|
|
) |
|
|
: rootInstance; |
|
|
|
|
|
|
|
|
if (!searchContext) return instance as Cheerio<Result>; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return searchContext.find(search) as Cheerio<Result>; |
|
|
} |
|
|
|
|
|
|
|
|
Object.assign(initialize, staticMethods, { |
|
|
load, |
|
|
|
|
|
_root: initialRoot, |
|
|
_options: internalOpts, |
|
|
|
|
|
fn: LoadedCheerio.prototype, |
|
|
|
|
|
prototype: LoadedCheerio.prototype, |
|
|
}); |
|
|
|
|
|
return initialize as CheerioAPI; |
|
|
}; |
|
|
} |
|
|
|
|
|
function isNode(obj: unknown): obj is AnyNode { |
|
|
return ( |
|
|
|
|
|
!!obj.name || |
|
|
|
|
|
obj.type === ElementType.Root || |
|
|
|
|
|
obj.type === ElementType.Text || |
|
|
|
|
|
obj.type === ElementType.Comment |
|
|
); |
|
|
} |
|
|
|