| |
| |
| |
| |
|
|
| export * from './load-parse.js'; |
| export { contains, merge } from './static.js'; |
| export type * from './types.js'; |
| export type { |
| Cheerio, |
| CheerioAPI, |
| CheerioOptions, |
| HTMLParser2Options, |
| } from './slim.js'; |
|
|
| import { adapter as htmlparser2Adapter } from 'parse5-htmlparser2-tree-adapter'; |
| import * as htmlparser2 from 'htmlparser2'; |
| import { ParserStream as Parse5Stream } from 'parse5-parser-stream'; |
| import { |
| decodeBuffer, |
| DecodeStream, |
| type SnifferOptions, |
| } from 'encoding-sniffer'; |
| import * as undici from 'undici'; |
| import MIMEType from 'whatwg-mimetype'; |
| import { Writable, finished } from 'node:stream'; |
| import type { CheerioAPI } from './load.js'; |
| import { |
| flattenOptions, |
| type InternalOptions, |
| type CheerioOptions, |
| } from './options.js'; |
| import { load } from './load-parse.js'; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| export function loadBuffer( |
| buffer: Buffer, |
| options: DecodeStreamOptions = {}, |
| ): CheerioAPI { |
| const opts = flattenOptions(options); |
| const str = decodeBuffer(buffer, { |
| defaultEncoding: opts?.xmlMode ? 'utf8' : 'windows-1252', |
| ...options.encoding, |
| }); |
|
|
| return load(str, opts); |
| } |
|
|
| function _stringStream( |
| options: InternalOptions | undefined, |
| cb: (err: Error | null | undefined, $: CheerioAPI) => void, |
| ): Writable { |
| if (options?._useHtmlParser2) { |
| const parser = htmlparser2.createDocumentStream( |
| (err, document) => cb(err, load(document)), |
| options, |
| ); |
|
|
| return new Writable({ |
| decodeStrings: false, |
| write(chunk, _encoding, callback) { |
| if (typeof chunk !== 'string') { |
| throw new TypeError('Expected a string'); |
| } |
|
|
| parser.write(chunk); |
| callback(); |
| }, |
| final(callback) { |
| parser.end(); |
| callback(); |
| }, |
| }); |
| } |
|
|
| options ??= {}; |
| options.treeAdapter ??= htmlparser2Adapter; |
|
|
| if (options.scriptingEnabled !== false) { |
| options.scriptingEnabled = true; |
| } |
|
|
| const stream = new Parse5Stream(options); |
|
|
| finished(stream, (err) => cb(err, load(stream.document))); |
|
|
| return stream; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| export function stringStream( |
| options: CheerioOptions, |
| cb: (err: Error | null | undefined, $: CheerioAPI) => void, |
| ): Writable { |
| return _stringStream(flattenOptions(options), cb); |
| } |
|
|
| export interface DecodeStreamOptions extends CheerioOptions { |
| encoding?: SnifferOptions; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| export function decodeStream( |
| options: DecodeStreamOptions, |
| cb: (err: Error | null | undefined, $: CheerioAPI) => void, |
| ): Writable { |
| const { encoding = {}, ...cheerioOptions } = options; |
| const opts = flattenOptions(cheerioOptions); |
|
|
| |
| encoding.defaultEncoding ??= opts?.xmlMode ? 'utf8' : 'windows-1252'; |
|
|
| const decodeStream = new DecodeStream(encoding); |
| const loadStream = _stringStream(opts, cb); |
|
|
| decodeStream.pipe(loadStream); |
|
|
| return decodeStream; |
| } |
|
|
| type UndiciStreamOptions = Parameters<typeof undici.stream>[1]; |
|
|
| export interface CheerioRequestOptions extends DecodeStreamOptions { |
| |
| requestOptions?: UndiciStreamOptions; |
| } |
|
|
| const defaultRequestOptions: UndiciStreamOptions = { |
| method: 'GET', |
| |
| maxRedirections: 5, |
| |
| throwOnError: true, |
| |
| headers: { |
| accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', |
| }, |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| export async function fromURL( |
| url: string | URL, |
| options: CheerioRequestOptions = {}, |
| ): Promise<CheerioAPI> { |
| const { |
| requestOptions = defaultRequestOptions, |
| encoding = {}, |
| ...cheerioOptions |
| } = options; |
| let undiciStream: Promise<undici.Dispatcher.StreamData> | undefined; |
|
|
| |
| requestOptions.headers ??= defaultRequestOptions.headers; |
|
|
| const promise = new Promise<CheerioAPI>((resolve, reject) => { |
| undiciStream = undici.stream(url, requestOptions, (res) => { |
| const contentType = res.headers['content-type'] ?? 'text/html'; |
| const mimeType = new MIMEType( |
| Array.isArray(contentType) ? contentType[0] : contentType, |
| ); |
|
|
| if (!mimeType.isHTML() && !mimeType.isXML()) { |
| throw new RangeError( |
| `The content-type "${contentType}" is neither HTML nor XML.`, |
| ); |
| } |
|
|
| |
| encoding.transportLayerEncodingLabel = mimeType.parameters.get('charset'); |
|
|
| |
| |
| |
| |
| const history = ( |
| res.context as |
| | { |
| history?: URL[]; |
| } |
| | undefined |
| )?.history; |
|
|
| const opts = { |
| encoding, |
| |
| xmlMode: mimeType.isXML(), |
| |
| baseURL: history ? history[history.length - 1] : url, |
| ...cheerioOptions, |
| }; |
|
|
| return decodeStream(opts, (err, $) => (err ? reject(err) : resolve($))); |
| }); |
| }); |
|
|
| |
| await undiciStream; |
|
|
| return promise; |
| } |
|
|