Initial commit: MangaReader iOS App
✨ Features: - App iOS completa para leer manga sin publicidad - Scraper con WKWebView para manhwaweb.com - Sistema de descargas offline - Lector con zoom y navegación - Favoritos y progreso de lectura - Compatible con iOS 15+ y Sideloadly/3uTools 📦 Contenido: - Backend Node.js con Puppeteer (opcional) - App iOS con SwiftUI - Scraper de capítulos e imágenes - Sistema de almacenamiento local - Testing completo - Documentación exhaustiva 🧪 Prueba: Capítulo 789 de One Piece descargado exitosamente - 21 páginas descargadas - 4.68 MB total - URLs verificadas y funcionales 🎉 Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
1145
backend/node_modules/cheerio/src/api/attributes.ts
generated
vendored
Normal file
1145
backend/node_modules/cheerio/src/api/attributes.ts
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
224
backend/node_modules/cheerio/src/api/css.ts
generated
vendored
Normal file
224
backend/node_modules/cheerio/src/api/css.ts
generated
vendored
Normal file
@@ -0,0 +1,224 @@
|
||||
import { domEach } from '../utils.js';
|
||||
import { isTag, type Element, type AnyNode } from 'domhandler';
|
||||
import type { Cheerio } from '../cheerio.js';
|
||||
|
||||
/**
|
||||
* Get the value of a style property for the first element in the set of matched
|
||||
* elements.
|
||||
*
|
||||
* @category CSS
|
||||
* @param names - Optionally the names of the properties of interest.
|
||||
* @returns A map of all of the style properties.
|
||||
* @see {@link https://api.jquery.com/css/}
|
||||
*/
|
||||
export function css<T extends AnyNode>(
|
||||
this: Cheerio<T>,
|
||||
names?: string[],
|
||||
): Record<string, string> | undefined;
|
||||
/**
|
||||
* Get the value of a style property for the first element in the set of matched
|
||||
* elements.
|
||||
*
|
||||
* @category CSS
|
||||
* @param name - The name of the property.
|
||||
* @returns The property value for the given name.
|
||||
* @see {@link https://api.jquery.com/css/}
|
||||
*/
|
||||
export function css<T extends AnyNode>(
|
||||
this: Cheerio<T>,
|
||||
name: string,
|
||||
): string | undefined;
|
||||
/**
|
||||
* Set one CSS property for every matched element.
|
||||
*
|
||||
* @category CSS
|
||||
* @param prop - The name of the property.
|
||||
* @param val - The new value.
|
||||
* @returns The instance itself.
|
||||
* @see {@link https://api.jquery.com/css/}
|
||||
*/
|
||||
export function css<T extends AnyNode>(
|
||||
this: Cheerio<T>,
|
||||
prop: string,
|
||||
val:
|
||||
| string
|
||||
| ((this: Element, i: number, style: string) => string | undefined),
|
||||
): Cheerio<T>;
|
||||
/**
|
||||
* Set multiple CSS properties for every matched element.
|
||||
*
|
||||
* @category CSS
|
||||
* @param map - A map of property names and values.
|
||||
* @returns The instance itself.
|
||||
* @see {@link https://api.jquery.com/css/}
|
||||
*/
|
||||
export function css<T extends AnyNode>(
|
||||
this: Cheerio<T>,
|
||||
map: Record<string, string>,
|
||||
): Cheerio<T>;
|
||||
/**
|
||||
* Set multiple CSS properties for every matched element.
|
||||
*
|
||||
* @category CSS
|
||||
* @param prop - The names of the properties.
|
||||
* @param val - The new values.
|
||||
* @returns The instance itself.
|
||||
* @see {@link https://api.jquery.com/css/}
|
||||
*/
|
||||
export function css<T extends AnyNode>(
|
||||
this: Cheerio<T>,
|
||||
prop?: string | string[] | Record<string, string>,
|
||||
val?:
|
||||
| string
|
||||
| ((this: Element, i: number, style: string) => string | undefined),
|
||||
): Cheerio<T> | Record<string, string> | string | undefined {
|
||||
if (
|
||||
(prop != null && val != null) ||
|
||||
// When `prop` is a "plain" object
|
||||
(typeof prop === 'object' && !Array.isArray(prop))
|
||||
) {
|
||||
return domEach(this, (el, i) => {
|
||||
if (isTag(el)) {
|
||||
// `prop` can't be an array here anymore.
|
||||
setCss(el, prop as string, val, i);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (this.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return getCss(this[0], prop as string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set styles of all elements.
|
||||
*
|
||||
* @private
|
||||
* @param el - Element to set style of.
|
||||
* @param prop - Name of property.
|
||||
* @param value - Value to set property to.
|
||||
* @param idx - Optional index within the selection.
|
||||
*/
|
||||
function setCss(
|
||||
el: Element,
|
||||
prop: string | Record<string, string>,
|
||||
value:
|
||||
| string
|
||||
| ((this: Element, i: number, style: string) => string | undefined)
|
||||
| undefined,
|
||||
idx: number,
|
||||
) {
|
||||
if (typeof prop === 'string') {
|
||||
const styles = getCss(el);
|
||||
|
||||
const val =
|
||||
typeof value === 'function' ? value.call(el, idx, styles[prop]) : value;
|
||||
|
||||
if (val === '') {
|
||||
delete styles[prop];
|
||||
} else if (val != null) {
|
||||
styles[prop] = val;
|
||||
}
|
||||
|
||||
el.attribs['style'] = stringify(styles);
|
||||
} else if (typeof prop === 'object') {
|
||||
const keys = Object.keys(prop);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const k = keys[i];
|
||||
setCss(el, k, prop[k], i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parsed styles of the first element.
|
||||
*
|
||||
* @private
|
||||
* @category CSS
|
||||
* @param el - Element to get styles from.
|
||||
* @param props - Optionally the names of the properties of interest.
|
||||
* @returns The parsed styles.
|
||||
*/
|
||||
function getCss(el: AnyNode, props?: string[]): Record<string, string>;
|
||||
/**
|
||||
* Get a property from the parsed styles of the first element.
|
||||
*
|
||||
* @private
|
||||
* @category CSS
|
||||
* @param el - Element to get styles from.
|
||||
* @param prop - Name of the prop.
|
||||
* @returns The value of the property.
|
||||
*/
|
||||
function getCss(el: AnyNode, prop: string): string | undefined;
|
||||
function getCss(
|
||||
el: AnyNode,
|
||||
prop?: string | string[],
|
||||
): Record<string, string> | string | undefined {
|
||||
if (!el || !isTag(el)) return;
|
||||
|
||||
const styles = parse(el.attribs['style']);
|
||||
if (typeof prop === 'string') {
|
||||
return styles[prop];
|
||||
}
|
||||
if (Array.isArray(prop)) {
|
||||
const newStyles: Record<string, string> = {};
|
||||
for (const item of prop) {
|
||||
if (styles[item] != null) {
|
||||
newStyles[item] = styles[item];
|
||||
}
|
||||
}
|
||||
return newStyles;
|
||||
}
|
||||
return styles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stringify `obj` to styles.
|
||||
*
|
||||
* @private
|
||||
* @category CSS
|
||||
* @param obj - Object to stringify.
|
||||
* @returns The serialized styles.
|
||||
*/
|
||||
function stringify(obj: Record<string, string>): string {
|
||||
return Object.keys(obj).reduce(
|
||||
(str, prop) => `${str}${str ? ' ' : ''}${prop}: ${obj[prop]};`,
|
||||
'',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse `styles`.
|
||||
*
|
||||
* @private
|
||||
* @category CSS
|
||||
* @param styles - Styles to be parsed.
|
||||
* @returns The parsed styles.
|
||||
*/
|
||||
function parse(styles: string): Record<string, string> {
|
||||
styles = (styles || '').trim();
|
||||
|
||||
if (!styles) return {};
|
||||
|
||||
const obj: Record<string, string> = {};
|
||||
|
||||
let key: string | undefined;
|
||||
|
||||
for (const str of styles.split(';')) {
|
||||
const n = str.indexOf(':');
|
||||
// If there is no :, or if it is the first/last character, add to the previous item's value
|
||||
if (n < 1 || n === str.length - 1) {
|
||||
const trimmed = str.trimEnd();
|
||||
if (trimmed.length > 0 && key !== undefined) {
|
||||
obj[key] += `;${trimmed}`;
|
||||
}
|
||||
} else {
|
||||
key = str.slice(0, n).trim();
|
||||
obj[key] = str.slice(n + 1).trim();
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
92
backend/node_modules/cheerio/src/api/extract.ts
generated
vendored
Normal file
92
backend/node_modules/cheerio/src/api/extract.ts
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
import type { AnyNode, Element } from 'domhandler';
|
||||
import type { Cheerio } from '../cheerio.js';
|
||||
import type { prop } from './attributes.js';
|
||||
|
||||
type ExtractDescriptorFn = (
|
||||
el: Element,
|
||||
key: string,
|
||||
// TODO: This could be typed with ExtractedMap
|
||||
obj: Record<string, unknown>,
|
||||
) => unknown;
|
||||
|
||||
interface ExtractDescriptor {
|
||||
selector: string;
|
||||
value?: string | ExtractDescriptorFn | ExtractMap;
|
||||
}
|
||||
|
||||
type ExtractValue = string | ExtractDescriptor | [string | ExtractDescriptor];
|
||||
|
||||
export type ExtractMap = Record<string, ExtractValue>;
|
||||
|
||||
type ExtractedValue<V extends ExtractValue> = V extends [
|
||||
string | ExtractDescriptor,
|
||||
]
|
||||
? NonNullable<ExtractedValue<V[0]>>[]
|
||||
: V extends string
|
||||
? string | undefined
|
||||
: V extends ExtractDescriptor
|
||||
? V['value'] extends infer U
|
||||
? U extends ExtractMap
|
||||
? ExtractedMap<U> | undefined
|
||||
: U extends ExtractDescriptorFn
|
||||
? ReturnType<U> | undefined
|
||||
: ReturnType<typeof prop> | undefined
|
||||
: never
|
||||
: never;
|
||||
|
||||
export type ExtractedMap<M extends ExtractMap> = {
|
||||
[key in keyof M]: ExtractedValue<M[key]>;
|
||||
};
|
||||
|
||||
function getExtractDescr(
|
||||
descr: string | ExtractDescriptor,
|
||||
): Required<ExtractDescriptor> {
|
||||
if (typeof descr === 'string') {
|
||||
return { selector: descr, value: 'textContent' };
|
||||
}
|
||||
|
||||
return {
|
||||
selector: descr.selector,
|
||||
value: descr.value ?? 'textContent',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract multiple values from a document, and store them in an object.
|
||||
*
|
||||
* @param map - An object containing key-value pairs. The keys are the names of
|
||||
* the properties to be created on the object, and the values are the
|
||||
* selectors to be used to extract the values.
|
||||
* @returns An object containing the extracted values.
|
||||
*/
|
||||
export function extract<M extends ExtractMap, T extends AnyNode>(
|
||||
this: Cheerio<T>,
|
||||
map: M,
|
||||
): ExtractedMap<M> {
|
||||
const ret: Record<string, unknown> = {};
|
||||
|
||||
for (const key in map) {
|
||||
const descr = map[key];
|
||||
const isArray = Array.isArray(descr);
|
||||
|
||||
const { selector, value } = getExtractDescr(isArray ? descr[0] : descr);
|
||||
|
||||
const fn: ExtractDescriptorFn =
|
||||
typeof value === 'function'
|
||||
? value
|
||||
: typeof value === 'string'
|
||||
? (el: Element) => this._make(el).prop(value)
|
||||
: (el: Element) => this._make(el).extract(value);
|
||||
|
||||
if (isArray) {
|
||||
ret[key] = this._findBySelector(selector, Number.POSITIVE_INFINITY)
|
||||
.map((_, el) => fn(el, key, ret))
|
||||
.get();
|
||||
} else {
|
||||
const $ = this._findBySelector(selector, 1);
|
||||
ret[key] = $.length > 0 ? fn($[0], key, ret) : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return ret as ExtractedMap<M>;
|
||||
}
|
||||
103
backend/node_modules/cheerio/src/api/forms.ts
generated
vendored
Normal file
103
backend/node_modules/cheerio/src/api/forms.ts
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
import { isTag, type AnyNode } from 'domhandler';
|
||||
import type { Cheerio } from '../cheerio.js';
|
||||
|
||||
/*
|
||||
* https://github.com/jquery/jquery/blob/2.1.3/src/manipulation/var/rcheckableType.js
|
||||
* https://github.com/jquery/jquery/blob/2.1.3/src/serialize.js
|
||||
*/
|
||||
const submittableSelector = 'input,select,textarea,keygen';
|
||||
const r20 = /%20/g;
|
||||
const rCRLF = /\r?\n/g;
|
||||
|
||||
/**
|
||||
* Encode a set of form elements as a string for submission.
|
||||
*
|
||||
* @category Forms
|
||||
* @example
|
||||
*
|
||||
* ```js
|
||||
* $('<form><input name="foo" value="bar" /></form>').serialize();
|
||||
* //=> 'foo=bar'
|
||||
* ```
|
||||
*
|
||||
* @returns The serialized form.
|
||||
* @see {@link https://api.jquery.com/serialize/}
|
||||
*/
|
||||
export function serialize<T extends AnyNode>(this: Cheerio<T>): string {
|
||||
// Convert form elements into name/value objects
|
||||
const arr = this.serializeArray();
|
||||
|
||||
// Serialize each element into a key/value string
|
||||
const retArr = arr.map(
|
||||
(data) =>
|
||||
`${encodeURIComponent(data.name)}=${encodeURIComponent(data.value)}`,
|
||||
);
|
||||
|
||||
// Return the resulting serialization
|
||||
return retArr.join('&').replace(r20, '+');
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a set of form elements as an array of names and values.
|
||||
*
|
||||
* @category Forms
|
||||
* @example
|
||||
*
|
||||
* ```js
|
||||
* $('<form><input name="foo" value="bar" /></form>').serializeArray();
|
||||
* //=> [ { name: 'foo', value: 'bar' } ]
|
||||
* ```
|
||||
*
|
||||
* @returns The serialized form.
|
||||
* @see {@link https://api.jquery.com/serializeArray/}
|
||||
*/
|
||||
export function serializeArray<T extends AnyNode>(
|
||||
this: Cheerio<T>,
|
||||
): {
|
||||
name: string;
|
||||
value: string;
|
||||
}[] {
|
||||
// Resolve all form elements from either forms or collections of form elements
|
||||
return this.map((_, elem) => {
|
||||
const $elem = this._make(elem);
|
||||
if (isTag(elem) && elem.name === 'form') {
|
||||
return $elem.find(submittableSelector).toArray();
|
||||
}
|
||||
return $elem.filter(submittableSelector).toArray();
|
||||
})
|
||||
.filter(
|
||||
// Verify elements have a name (`attr.name`) and are not disabled (`:enabled`)
|
||||
'[name!=""]:enabled' +
|
||||
// And cannot be clicked (`[type=submit]`) or are used in `x-www-form-urlencoded` (`[type=file]`)
|
||||
':not(:submit, :button, :image, :reset, :file)' +
|
||||
// And are either checked/don't have a checkable state
|
||||
':matches([checked], :not(:checkbox, :radio))',
|
||||
// Convert each of the elements to its value(s)
|
||||
)
|
||||
.map<
|
||||
AnyNode,
|
||||
{
|
||||
name: string;
|
||||
value: string;
|
||||
}
|
||||
>((_, elem) => {
|
||||
const $elem = this._make(elem);
|
||||
const name = $elem.attr('name')!; // We have filtered for elements with a name before.
|
||||
// If there is no value set (e.g. `undefined`, `null`), then default value to empty
|
||||
const value = $elem.val() ?? '';
|
||||
|
||||
// If we have an array of values (e.g. `<select multiple>`), return an array of key/value pairs
|
||||
if (Array.isArray(value)) {
|
||||
return value.map((val) =>
|
||||
/*
|
||||
* We trim replace any line endings (e.g. `\r` or `\r\n` with `\r\n`) to guarantee consistency across platforms
|
||||
* These can occur inside of `<textarea>'s`
|
||||
*/
|
||||
({ name, value: val.replace(rCRLF, '\r\n') }),
|
||||
);
|
||||
}
|
||||
// Otherwise (e.g. `<input type="text">`, return only one key/value pair
|
||||
return { name, value: value.replace(rCRLF, '\r\n') };
|
||||
})
|
||||
.toArray();
|
||||
}
|
||||
1115
backend/node_modules/cheerio/src/api/manipulation.ts
generated
vendored
Normal file
1115
backend/node_modules/cheerio/src/api/manipulation.ts
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1175
backend/node_modules/cheerio/src/api/traversing.ts
generated
vendored
Normal file
1175
backend/node_modules/cheerio/src/api/traversing.ts
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
143
backend/node_modules/cheerio/src/cheerio.ts
generated
vendored
Normal file
143
backend/node_modules/cheerio/src/cheerio.ts
generated
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */
|
||||
import type { InternalOptions } from './options.js';
|
||||
import type { AnyNode, Document, ParentNode } from 'domhandler';
|
||||
import type { BasicAcceptedElems } from './types.js';
|
||||
|
||||
import * as Attributes from './api/attributes.js';
|
||||
import * as Traversing from './api/traversing.js';
|
||||
import * as Manipulation from './api/manipulation.js';
|
||||
import * as Css from './api/css.js';
|
||||
import * as Forms from './api/forms.js';
|
||||
import * as Extract from './api/extract.js';
|
||||
|
||||
type MethodsType = typeof Attributes &
|
||||
typeof Traversing &
|
||||
typeof Manipulation &
|
||||
typeof Css &
|
||||
typeof Forms &
|
||||
typeof Extract;
|
||||
|
||||
/**
|
||||
* The cheerio class is the central class of the library. It wraps a set of
|
||||
* elements and provides an API for traversing, modifying, and interacting with
|
||||
* the set.
|
||||
*
|
||||
* Loading a document will return the Cheerio class bound to the root element of
|
||||
* the document. The class will be instantiated when querying the document (when
|
||||
* calling `$('selector')`).
|
||||
*
|
||||
* @example This is the HTML markup we will be using in all of the API examples:
|
||||
*
|
||||
* ```html
|
||||
* <ul id="fruits">
|
||||
* <li class="apple">Apple</li>
|
||||
* <li class="orange">Orange</li>
|
||||
* <li class="pear">Pear</li>
|
||||
* </ul>
|
||||
* ```
|
||||
*/
|
||||
export abstract class Cheerio<T> implements ArrayLike<T> {
|
||||
length = 0;
|
||||
[index: number]: T;
|
||||
|
||||
options: InternalOptions;
|
||||
/**
|
||||
* The root of the document. Can be set by using the `root` argument of the
|
||||
* constructor.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_root: Cheerio<Document> | null;
|
||||
|
||||
/**
|
||||
* Instance of cheerio. Methods are specified in the modules. Usage of this
|
||||
* constructor is not recommended. Please use `$.load` instead.
|
||||
*
|
||||
* @private
|
||||
* @param elements - The new selection.
|
||||
* @param root - Sets the root node.
|
||||
* @param options - Options for the instance.
|
||||
*/
|
||||
constructor(
|
||||
elements: ArrayLike<T> | undefined,
|
||||
root: Cheerio<Document> | null,
|
||||
options: InternalOptions,
|
||||
) {
|
||||
this.options = options;
|
||||
this._root = root;
|
||||
|
||||
if (elements) {
|
||||
for (let idx = 0; idx < elements.length; idx++) {
|
||||
this[idx] = elements[idx];
|
||||
}
|
||||
this.length = elements.length;
|
||||
}
|
||||
}
|
||||
|
||||
prevObject: Cheerio<any> | undefined;
|
||||
/**
|
||||
* Make a cheerio object.
|
||||
*
|
||||
* @private
|
||||
* @param dom - The contents of the new object.
|
||||
* @param context - The context of the new object.
|
||||
* @returns The new cheerio object.
|
||||
*/
|
||||
abstract _make<T>(
|
||||
dom: ArrayLike<T> | T | string,
|
||||
context?: BasicAcceptedElems<AnyNode>,
|
||||
): Cheerio<T>;
|
||||
|
||||
/**
|
||||
* Parses some content.
|
||||
*
|
||||
* @private
|
||||
* @param content - Content to parse.
|
||||
* @param options - Options for parsing.
|
||||
* @param isDocument - Allows parser to be switched to fragment mode.
|
||||
* @returns A document containing the `content`.
|
||||
*/
|
||||
abstract _parse(
|
||||
content: string | Document | AnyNode | AnyNode[] | Buffer,
|
||||
options: InternalOptions,
|
||||
isDocument: boolean,
|
||||
context: ParentNode | null,
|
||||
): Document;
|
||||
|
||||
/**
|
||||
* Render an element or a set of elements.
|
||||
*
|
||||
* @private
|
||||
* @param dom - DOM to render.
|
||||
* @returns The rendered DOM.
|
||||
*/
|
||||
abstract _render(dom: AnyNode | ArrayLike<AnyNode>): string;
|
||||
}
|
||||
|
||||
export interface Cheerio<T> extends MethodsType, Iterable<T> {
|
||||
cheerio: '[cheerio object]';
|
||||
|
||||
splice: typeof Array.prototype.splice;
|
||||
}
|
||||
|
||||
/** Set a signature of the object. */
|
||||
Cheerio.prototype.cheerio = '[cheerio object]';
|
||||
|
||||
/*
|
||||
* Make cheerio an array-like object
|
||||
*/
|
||||
Cheerio.prototype.splice = Array.prototype.splice;
|
||||
|
||||
// Support for (const element of $(...)) iteration:
|
||||
Cheerio.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
|
||||
|
||||
// Plug in the API
|
||||
Object.assign(
|
||||
Cheerio.prototype,
|
||||
Attributes,
|
||||
Traversing,
|
||||
Manipulation,
|
||||
Css,
|
||||
Forms,
|
||||
Extract,
|
||||
);
|
||||
10
backend/node_modules/cheerio/src/index-browser.mts
generated
vendored
Normal file
10
backend/node_modules/cheerio/src/index-browser.mts
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
export type * from './types.js';
|
||||
export type {
|
||||
Cheerio,
|
||||
CheerioAPI,
|
||||
CheerioOptions,
|
||||
HTMLParser2Options,
|
||||
} from './slim.js';
|
||||
export { contains, merge } from './static.js';
|
||||
|
||||
export * from './load-parse.js';
|
||||
294
backend/node_modules/cheerio/src/index.ts
generated
vendored
Normal file
294
backend/node_modules/cheerio/src/index.ts
generated
vendored
Normal file
@@ -0,0 +1,294 @@
|
||||
/**
|
||||
* @file Batteries-included version of Cheerio. This module includes several
|
||||
* convenience methods for loading documents from various sources.
|
||||
*/
|
||||
|
||||
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';
|
||||
|
||||
/**
|
||||
* Sniffs the encoding of a buffer, then creates a querying function bound to a
|
||||
* document created from the buffer.
|
||||
*
|
||||
* @category Loading
|
||||
* @example
|
||||
*
|
||||
* ```js
|
||||
* import * as cheerio from 'cheerio';
|
||||
*
|
||||
* const buffer = fs.readFileSync('index.html');
|
||||
* const $ = cheerio.loadBuffer(buffer);
|
||||
* ```
|
||||
*
|
||||
* @param buffer - The buffer to sniff the encoding of.
|
||||
* @param options - The options to pass to Cheerio.
|
||||
* @returns The loaded document.
|
||||
*/
|
||||
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)),
|
||||
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, options)));
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a stream that parses a sequence of strings into a document.
|
||||
*
|
||||
* The stream is a `Writable` stream that accepts strings. When the stream is
|
||||
* finished, the callback is called with the loaded document.
|
||||
*
|
||||
* @category Loading
|
||||
* @example
|
||||
*
|
||||
* ```js
|
||||
* import * as cheerio from 'cheerio';
|
||||
* import * as fs from 'fs';
|
||||
*
|
||||
* const writeStream = cheerio.stringStream({}, (err, $) => {
|
||||
* if (err) {
|
||||
* // Handle error
|
||||
* }
|
||||
*
|
||||
* console.log($('h1').text());
|
||||
* // Output: Hello, world!
|
||||
* });
|
||||
*
|
||||
* fs.createReadStream('my-document.html', { encoding: 'utf8' }).pipe(
|
||||
* writeStream,
|
||||
* );
|
||||
* ```
|
||||
*
|
||||
* @param options - The options to pass to Cheerio.
|
||||
* @param cb - The callback to call when the stream is finished.
|
||||
* @returns The writable 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a stream of buffers into a document.
|
||||
*
|
||||
* The stream is a `Writable` stream that accepts buffers. When the stream is
|
||||
* finished, the callback is called with the loaded document.
|
||||
*
|
||||
* @category Loading
|
||||
* @param options - The options to pass to Cheerio.
|
||||
* @param cb - The callback to call when the stream is finished.
|
||||
* @returns The writable stream.
|
||||
*/
|
||||
export function decodeStream(
|
||||
options: DecodeStreamOptions,
|
||||
cb: (err: Error | null | undefined, $: CheerioAPI) => void,
|
||||
): Writable {
|
||||
const { encoding = {}, ...cheerioOptions } = options;
|
||||
const opts = flattenOptions(cheerioOptions);
|
||||
|
||||
// Set the default encoding to UTF-8 for XML mode
|
||||
encoding.defaultEncoding ??= opts?.xmlMode ? 'utf8' : 'windows-1252';
|
||||
|
||||
const decodeStream = new DecodeStream(encoding);
|
||||
const loadStream = _stringStream(opts, cb);
|
||||
|
||||
decodeStream.pipe(loadStream);
|
||||
|
||||
return decodeStream;
|
||||
}
|
||||
|
||||
type UndiciStreamOptions = Omit<
|
||||
undici.Dispatcher.RequestOptions<unknown>,
|
||||
'path'
|
||||
>;
|
||||
|
||||
export interface CheerioRequestOptions extends DecodeStreamOptions {
|
||||
/** The options passed to `undici`'s `stream` method. */
|
||||
requestOptions?: UndiciStreamOptions;
|
||||
}
|
||||
|
||||
const defaultRequestOptions: UndiciStreamOptions = {
|
||||
method: 'GET',
|
||||
// Set an Accept header
|
||||
headers: {
|
||||
accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* `fromURL` loads a document from a URL.
|
||||
*
|
||||
* By default, redirects are allowed and non-2xx responses are rejected.
|
||||
*
|
||||
* @category Loading
|
||||
* @example
|
||||
*
|
||||
* ```js
|
||||
* import * as cheerio from 'cheerio';
|
||||
*
|
||||
* const $ = await cheerio.fromURL('https://example.com');
|
||||
* ```
|
||||
*
|
||||
* @param url - The URL to load the document from.
|
||||
* @param options - The options to pass to Cheerio.
|
||||
* @returns The loaded document.
|
||||
*/
|
||||
export async function fromURL(
|
||||
url: string | URL,
|
||||
options: CheerioRequestOptions = {},
|
||||
): Promise<CheerioAPI> {
|
||||
const {
|
||||
requestOptions = defaultRequestOptions,
|
||||
encoding = {},
|
||||
...cheerioOptions
|
||||
} = options;
|
||||
let undiciStream: Promise<undici.Dispatcher.StreamData<unknown>> | undefined;
|
||||
|
||||
// Add headers if none were supplied.
|
||||
const urlObject = typeof url === 'string' ? new URL(url) : url;
|
||||
const streamOptions = {
|
||||
headers: defaultRequestOptions.headers,
|
||||
path: urlObject.pathname + urlObject.search,
|
||||
...requestOptions,
|
||||
};
|
||||
|
||||
const promise = new Promise<CheerioAPI>((resolve, reject) => {
|
||||
undiciStream = new undici.Client(urlObject.origin)
|
||||
.compose(undici.interceptors.redirect({ maxRedirections: 5 }))
|
||||
.stream(streamOptions, (res) => {
|
||||
if (res.statusCode < 200 || res.statusCode >= 300) {
|
||||
throw new undici.errors.ResponseError(
|
||||
'Response Error',
|
||||
res.statusCode,
|
||||
{
|
||||
headers: res.headers,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const contentTypeHeader = res.headers['content-type'] ?? 'text/html';
|
||||
const mimeType = new MIMEType(
|
||||
Array.isArray(contentTypeHeader)
|
||||
? contentTypeHeader[0]
|
||||
: contentTypeHeader,
|
||||
);
|
||||
|
||||
if (!mimeType.isHTML() && !mimeType.isXML()) {
|
||||
throw new RangeError(
|
||||
`The content-type "${mimeType.essence}" is neither HTML nor XML.`,
|
||||
);
|
||||
}
|
||||
|
||||
// Forward the charset from the header to the decodeStream.
|
||||
encoding.transportLayerEncodingLabel =
|
||||
mimeType.parameters.get('charset');
|
||||
|
||||
/*
|
||||
* If we allow redirects, we will have entries in the history.
|
||||
* The last entry will be the final URL.
|
||||
*/
|
||||
const history = (
|
||||
res.context as
|
||||
| {
|
||||
history?: URL[];
|
||||
}
|
||||
| undefined
|
||||
)?.history;
|
||||
// Set the `baseURI` to the final URL.
|
||||
const baseURI = history ? history[history.length - 1] : urlObject;
|
||||
|
||||
const opts: DecodeStreamOptions = {
|
||||
encoding,
|
||||
// Set XML mode based on the MIME type.
|
||||
xmlMode: mimeType.isXML(),
|
||||
baseURI,
|
||||
...cheerioOptions,
|
||||
};
|
||||
|
||||
return decodeStream(opts, (err, $) => (err ? reject(err) : resolve($)));
|
||||
});
|
||||
});
|
||||
|
||||
// Let's make sure the request is completed before returning the promise.
|
||||
await undiciStream;
|
||||
|
||||
return promise;
|
||||
}
|
||||
39
backend/node_modules/cheerio/src/load-parse.ts
generated
vendored
Normal file
39
backend/node_modules/cheerio/src/load-parse.ts
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
import { type CheerioAPI, getLoad } from './load.js';
|
||||
import { getParse } from './parse.js';
|
||||
import { renderWithParse5, parseWithParse5 } from './parsers/parse5-adapter.js';
|
||||
import type { CheerioOptions } from './options.js';
|
||||
import renderWithHtmlparser2 from 'dom-serializer';
|
||||
import { parseDocument as parseWithHtmlparser2 } from 'htmlparser2';
|
||||
import type { AnyNode } from 'domhandler';
|
||||
|
||||
const parse = getParse((content, options, isDocument, context) =>
|
||||
options._useHtmlParser2
|
||||
? parseWithHtmlparser2(content, options)
|
||||
: parseWithParse5(content, options, isDocument, context),
|
||||
);
|
||||
|
||||
// Duplicate docs due to https://github.com/TypeStrong/typedoc/issues/1616
|
||||
/**
|
||||
* Create a querying function, bound to a document created from the provided
|
||||
* markup.
|
||||
*
|
||||
* Note that similar to web browser contexts, this operation may introduce
|
||||
* `<html>`, `<head>`, and `<body>` elements; set `isDocument` to `false` to
|
||||
* switch to fragment mode and disable this.
|
||||
*
|
||||
* @category Loading
|
||||
* @param content - Markup to be loaded.
|
||||
* @param options - Options for the created instance.
|
||||
* @param isDocument - Allows parser to be switched to fragment mode.
|
||||
* @returns The loaded document.
|
||||
* @see {@link https://cheerio.js.org/docs/basics/loading#load} for additional usage information.
|
||||
*/
|
||||
export const load: (
|
||||
content: string | AnyNode | AnyNode[] | Buffer,
|
||||
options?: CheerioOptions | null,
|
||||
isDocument?: boolean,
|
||||
) => CheerioAPI = getLoad(parse, (dom, options) =>
|
||||
options._useHtmlParser2
|
||||
? renderWithHtmlparser2(dom, options)
|
||||
: renderWithParse5(dom),
|
||||
);
|
||||
282
backend/node_modules/cheerio/src/load.ts
generated
vendored
Normal file
282
backend/node_modules/cheerio/src/load.ts
generated
vendored
Normal file
@@ -0,0 +1,282 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* A querying function, bound to a document created from the provided markup.
|
||||
*
|
||||
* Also provides several helper methods for dealing with the document as a
|
||||
* whole.
|
||||
*/
|
||||
export interface CheerioAPI extends StaticType {
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* `selector` searches within the `context` scope, which searches within the
|
||||
* `root` scope.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```js
|
||||
* $('ul .pear').attr('class');
|
||||
* //=> pear
|
||||
*
|
||||
* $('li[class=orange]').html();
|
||||
* //=> Orange
|
||||
*
|
||||
* $('.apple', '#fruits').text();
|
||||
* //=> Apple
|
||||
* ```
|
||||
*
|
||||
* Optionally, you can also load HTML by passing the string as the selector:
|
||||
*
|
||||
* ```js
|
||||
* $('<ul id="fruits">...</ul>');
|
||||
* ```
|
||||
*
|
||||
* Or the context:
|
||||
*
|
||||
* ```js
|
||||
* $('ul', '<ul id="fruits">...</ul>');
|
||||
* ```
|
||||
*
|
||||
* Or as the root:
|
||||
*
|
||||
* ```js
|
||||
* $('li', 'ul', '<ul id="fruits">...</ul>');
|
||||
* ```
|
||||
*
|
||||
* @param selector - Either a selector to look for within the document, or the
|
||||
* contents of a new Cheerio instance.
|
||||
* @param context - Either a selector to look for within the root, or the
|
||||
* contents of the document to query.
|
||||
* @param root - Optional HTML document string.
|
||||
*/
|
||||
<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>;
|
||||
|
||||
/**
|
||||
* The root the document was originally loaded with.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_root: Document;
|
||||
|
||||
/**
|
||||
* The options the document was originally loaded with.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_options: InternalOptions;
|
||||
|
||||
/** Mimic jQuery's prototype alias for plugin authors. */
|
||||
fn: typeof Cheerio.prototype;
|
||||
|
||||
/**
|
||||
* The `.load` static method defined on the "loaded" Cheerio factory function
|
||||
* is deprecated. Users are encouraged to instead use the `load` function
|
||||
* exported by the Cheerio module.
|
||||
*
|
||||
* @deprecated Use the `load` function exported by the Cheerio module.
|
||||
* @category Deprecated
|
||||
* @example
|
||||
*
|
||||
* ```js
|
||||
* const $ = cheerio.load('<h1>Hello, <span>world</span>.</h1>');
|
||||
* ```
|
||||
*/
|
||||
load: ReturnType<typeof getLoad>;
|
||||
}
|
||||
|
||||
export function getLoad(
|
||||
parse: Cheerio<AnyNode>['_parse'],
|
||||
render: (
|
||||
dom: AnyNode | ArrayLike<AnyNode>,
|
||||
options: InternalOptions,
|
||||
) => string,
|
||||
) {
|
||||
/**
|
||||
* Create a querying function, bound to a document created from the provided
|
||||
* markup.
|
||||
*
|
||||
* Note that similar to web browser contexts, this operation may introduce
|
||||
* `<html>`, `<head>`, and `<body>` elements; set `isDocument` to `false` to
|
||||
* switch to fragment mode and disable this.
|
||||
*
|
||||
* @param content - Markup to be loaded.
|
||||
* @param options - Options for the created instance.
|
||||
* @param isDocument - Allows parser to be switched to fragment mode.
|
||||
* @returns The loaded document.
|
||||
* @see {@link https://cheerio.js.org/docs/basics/loading#load} for additional usage information.
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* Create an extended class here, so that extensions only live on one
|
||||
* instance.
|
||||
*/
|
||||
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);
|
||||
// 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: AnyNode[] | undefined =
|
||||
typeof selector === 'string' && isHtml(selector)
|
||||
? // $(<html>)
|
||||
parse(selector, options, false, null).children
|
||||
: isNode(selector)
|
||||
? // $(dom)
|
||||
[selector]
|
||||
: Array.isArray(selector)
|
||||
? // $([dom])
|
||||
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');
|
||||
}
|
||||
|
||||
// We know that our selector is a string now.
|
||||
let search = selector;
|
||||
|
||||
const searchContext: Cheerio<AnyNode> | undefined = context
|
||||
? // If we don't have a context, maybe we have a root, from loading
|
||||
typeof context === 'string'
|
||||
? isHtml(context)
|
||||
? // $('li', '<ul>...</ul>')
|
||||
new LoadedCheerio<Document>(
|
||||
[parse(context, options, false, null)],
|
||||
rootInstance,
|
||||
options,
|
||||
)
|
||||
: // $('li', 'ul')
|
||||
((search = `${context} ${search}` as S), rootInstance)
|
||||
: isCheerio<AnyNode>(context)
|
||||
? // $('li', $)
|
||||
context
|
||||
: // $('li', node), $('li', [nodes])
|
||||
new LoadedCheerio<AnyNode>(
|
||||
Array.isArray(context) ? context : [context],
|
||||
rootInstance,
|
||||
options,
|
||||
)
|
||||
: rootInstance;
|
||||
|
||||
// If we still don't have a context, return
|
||||
if (!searchContext) return instance as Cheerio<Result>;
|
||||
|
||||
/*
|
||||
* #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: initialRoot,
|
||||
_options: internalOpts,
|
||||
// Add `fn` for plugins
|
||||
fn: LoadedCheerio.prototype,
|
||||
// Add the prototype here to maintain `instanceof` behavior.
|
||||
prototype: LoadedCheerio.prototype,
|
||||
});
|
||||
|
||||
return initialize as CheerioAPI;
|
||||
};
|
||||
}
|
||||
|
||||
function isNode(obj: unknown): obj is AnyNode {
|
||||
return (
|
||||
// @ts-expect-error: TS doesn't know about the `name` property.
|
||||
!!obj.name ||
|
||||
// @ts-expect-error: TS doesn't know about the `type` property.
|
||||
obj.type === ElementType.Root ||
|
||||
// @ts-expect-error: TS doesn't know about the `type` property.
|
||||
obj.type === ElementType.Text ||
|
||||
// @ts-expect-error: TS doesn't know about the `type` property.
|
||||
obj.type === ElementType.Comment
|
||||
);
|
||||
}
|
||||
136
backend/node_modules/cheerio/src/options.ts
generated
vendored
Normal file
136
backend/node_modules/cheerio/src/options.ts
generated
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
import type { DomHandlerOptions } from 'domhandler';
|
||||
import type { ParserOptions as HTMLParser2ParserOptions } from 'htmlparser2';
|
||||
import type { ParserOptions as Parse5ParserOptions } from 'parse5';
|
||||
import type { Htmlparser2TreeAdapterMap } from 'parse5-htmlparser2-tree-adapter';
|
||||
import type { Options as SelectOptions } from 'cheerio-select';
|
||||
import type { DomSerializerOptions } from 'dom-serializer';
|
||||
|
||||
/**
|
||||
* Options accepted by htmlparser2, the default parser for XML.
|
||||
*
|
||||
* @see https://github.com/fb55/htmlparser2/wiki/Parser-options
|
||||
*/
|
||||
export interface HTMLParser2Options
|
||||
extends DomHandlerOptions, DomSerializerOptions, HTMLParser2ParserOptions {
|
||||
/** Treat the input as an XML document. */
|
||||
xmlMode?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options accepted by Cheerio.
|
||||
*
|
||||
* Please note that parser-specific options are _only recognized_ if the
|
||||
* relevant parser is used.
|
||||
*/
|
||||
export interface CheerioOptions extends Parse5ParserOptions<Htmlparser2TreeAdapterMap> {
|
||||
/**
|
||||
* Recommended way of configuring htmlparser2 when wanting to parse XML.
|
||||
*
|
||||
* This will switch Cheerio to use htmlparser2.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
xml?: HTMLParser2Options | boolean;
|
||||
|
||||
/**
|
||||
* Enable xml mode, which will switch Cheerio to use htmlparser2.
|
||||
*
|
||||
* @deprecated Please use the `xml` option instead.
|
||||
* @default false
|
||||
*/
|
||||
xmlMode?: boolean;
|
||||
|
||||
/** The base URI for the document. Used to resolve the `href` and `src` props. */
|
||||
baseURI?: string | URL;
|
||||
|
||||
/**
|
||||
* Is the document in quirks mode?
|
||||
*
|
||||
* This will lead to `.className` and `#id` being case-insensitive.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
quirksMode?: SelectOptions['quirksMode'];
|
||||
/**
|
||||
* Extension point for pseudo-classes.
|
||||
*
|
||||
* Maps from names to either strings of functions.
|
||||
*
|
||||
* - A string value is a selector that the element must match to be selected.
|
||||
* - A function is called with the element as its first argument, and optional
|
||||
* parameters second. If it returns true, the element is selected.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```js
|
||||
* const $ = cheerio.load(
|
||||
* '<div class="foo"></div><div data-bar="boo"></div>',
|
||||
* {
|
||||
* pseudos: {
|
||||
* // `:foo` is an alias for `div.foo`
|
||||
* foo: 'div.foo',
|
||||
* // `:bar(val)` is equivalent to `[data-bar=val s]`
|
||||
* bar: (el, val) => el.attribs['data-bar'] === val,
|
||||
* },
|
||||
* },
|
||||
* );
|
||||
*
|
||||
* $(':foo').length; // 1
|
||||
* $('div:bar(boo)').length; // 1
|
||||
* $('div:bar(baz)').length; // 0
|
||||
* ```
|
||||
*/
|
||||
pseudos?: SelectOptions['pseudos'];
|
||||
}
|
||||
|
||||
/** Internal options for Cheerio. */
|
||||
export interface InternalOptions
|
||||
extends HTMLParser2Options, Omit<CheerioOptions, 'xml'> {
|
||||
/**
|
||||
* Whether to use htmlparser2.
|
||||
*
|
||||
* This is set to true if `xml` is set to true.
|
||||
*/
|
||||
_useHtmlParser2?: boolean;
|
||||
}
|
||||
|
||||
const defaultOpts: InternalOptions = {
|
||||
_useHtmlParser2: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* Flatten the options for Cheerio.
|
||||
*
|
||||
* This will set `_useHtmlParser2` to true if `xml` is set to true.
|
||||
*
|
||||
* @param options - The options to flatten.
|
||||
* @param baseOptions - The base options to use.
|
||||
* @returns The flattened options.
|
||||
*/
|
||||
export function flattenOptions(
|
||||
options?: CheerioOptions | null,
|
||||
baseOptions?: InternalOptions,
|
||||
): InternalOptions {
|
||||
if (!options) {
|
||||
return baseOptions ?? defaultOpts;
|
||||
}
|
||||
|
||||
const opts: InternalOptions = {
|
||||
_useHtmlParser2: !!options.xmlMode,
|
||||
...baseOptions,
|
||||
...options,
|
||||
};
|
||||
|
||||
if (options.xml) {
|
||||
opts._useHtmlParser2 = true;
|
||||
opts.xmlMode = true;
|
||||
|
||||
if (options.xml !== true) {
|
||||
Object.assign(opts, options.xml);
|
||||
}
|
||||
} else if (options.xmlMode) {
|
||||
opts._useHtmlParser2 = true;
|
||||
}
|
||||
|
||||
return opts;
|
||||
}
|
||||
105
backend/node_modules/cheerio/src/parse.ts
generated
vendored
Normal file
105
backend/node_modules/cheerio/src/parse.ts
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
import { removeElement } from 'domutils';
|
||||
import {
|
||||
type AnyNode,
|
||||
Document,
|
||||
type ParentNode,
|
||||
isDocument as checkIsDocument,
|
||||
} from 'domhandler';
|
||||
import type { InternalOptions } from './options.js';
|
||||
|
||||
/**
|
||||
* Get the parse function with options.
|
||||
*
|
||||
* @param parser - The parser function.
|
||||
* @returns The parse function with options.
|
||||
*/
|
||||
export function getParse(
|
||||
parser: (
|
||||
content: string,
|
||||
options: InternalOptions,
|
||||
isDocument: boolean,
|
||||
context: ParentNode | null,
|
||||
) => Document,
|
||||
) {
|
||||
/**
|
||||
* Parse a HTML string or a node.
|
||||
*
|
||||
* @param content - The HTML string or node.
|
||||
* @param options - The parser options.
|
||||
* @param isDocument - If `content` is a document.
|
||||
* @param context - The context node in the DOM tree.
|
||||
* @returns The parsed document node.
|
||||
*/
|
||||
return function parse(
|
||||
content: string | Document | AnyNode | AnyNode[] | Buffer,
|
||||
options: InternalOptions,
|
||||
isDocument: boolean,
|
||||
context: ParentNode | null,
|
||||
): Document {
|
||||
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(content)) {
|
||||
content = content.toString();
|
||||
}
|
||||
|
||||
if (typeof content === 'string') {
|
||||
return parser(content, options, isDocument, context);
|
||||
}
|
||||
|
||||
const doc = content as AnyNode | AnyNode[] | Document;
|
||||
|
||||
if (!Array.isArray(doc) && checkIsDocument(doc)) {
|
||||
// If `doc` is already a root, just return it
|
||||
return doc;
|
||||
}
|
||||
|
||||
// Add content to new root element
|
||||
const root = new Document([]);
|
||||
|
||||
// Update the DOM using the root
|
||||
update(doc, root);
|
||||
|
||||
return root;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the dom structure, for one changed layer.
|
||||
*
|
||||
* @param newChilds - The new children.
|
||||
* @param parent - The new parent.
|
||||
* @returns The parent node.
|
||||
*/
|
||||
export function update(
|
||||
newChilds: AnyNode[] | AnyNode,
|
||||
parent: ParentNode | null,
|
||||
): ParentNode | null {
|
||||
// Normalize
|
||||
const arr = Array.isArray(newChilds) ? newChilds : [newChilds];
|
||||
|
||||
// Update parent
|
||||
if (parent) {
|
||||
parent.children = arr;
|
||||
} else {
|
||||
parent = null;
|
||||
}
|
||||
|
||||
// Update neighbors
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
const node = arr[i];
|
||||
|
||||
// Cleanly remove existing nodes from their previous structures.
|
||||
if (node.parent && node.parent.children !== arr) {
|
||||
removeElement(node);
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
node.prev = arr[i - 1] || null;
|
||||
node.next = arr[i + 1] || null;
|
||||
} else {
|
||||
node.prev = node.next = null;
|
||||
}
|
||||
|
||||
node.parent = parent;
|
||||
}
|
||||
|
||||
return parent;
|
||||
}
|
||||
66
backend/node_modules/cheerio/src/parsers/parse5-adapter.ts
generated
vendored
Normal file
66
backend/node_modules/cheerio/src/parsers/parse5-adapter.ts
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
import {
|
||||
type AnyNode,
|
||||
type Document,
|
||||
type ParentNode,
|
||||
isDocument,
|
||||
} from 'domhandler';
|
||||
import { parse as parseDocument, parseFragment, serializeOuter } from 'parse5';
|
||||
import { adapter as htmlparser2Adapter } from 'parse5-htmlparser2-tree-adapter';
|
||||
import type { InternalOptions } from '../options.js';
|
||||
|
||||
/**
|
||||
* Parse the content with `parse5` in the context of the given `ParentNode`.
|
||||
*
|
||||
* @param content - The content to parse.
|
||||
* @param options - A set of options to use to parse.
|
||||
* @param isDocument - Whether to parse the content as a full HTML document.
|
||||
* @param context - The context in which to parse the content.
|
||||
* @returns The parsed content.
|
||||
*/
|
||||
export function parseWithParse5(
|
||||
content: string,
|
||||
options: InternalOptions,
|
||||
isDocument: boolean,
|
||||
context: ParentNode | null,
|
||||
): Document {
|
||||
options.treeAdapter ??= htmlparser2Adapter;
|
||||
|
||||
if (options.scriptingEnabled !== false) {
|
||||
options.scriptingEnabled = true;
|
||||
}
|
||||
|
||||
return isDocument
|
||||
? parseDocument(content, options)
|
||||
: parseFragment(context, content, options);
|
||||
}
|
||||
|
||||
const renderOpts = { treeAdapter: htmlparser2Adapter };
|
||||
|
||||
/**
|
||||
* Renders the given DOM tree with `parse5` and returns the result as a string.
|
||||
*
|
||||
* @param dom - The DOM tree to render.
|
||||
* @returns The rendered document.
|
||||
*/
|
||||
export function renderWithParse5(dom: AnyNode | ArrayLike<AnyNode>): string {
|
||||
/*
|
||||
* `dom-serializer` passes over the special "root" node and renders the
|
||||
* node's children in its place. To mimic this behavior with `parse5`, an
|
||||
* equivalent operation must be applied to the input array.
|
||||
*/
|
||||
const nodes = 'length' in dom ? dom : [dom];
|
||||
for (let index = 0; index < nodes.length; index += 1) {
|
||||
const node = nodes[index];
|
||||
if (isDocument(node)) {
|
||||
Array.prototype.splice.call(nodes, index, 1, ...node.children);
|
||||
}
|
||||
}
|
||||
|
||||
let result = '';
|
||||
for (let index = 0; index < nodes.length; index += 1) {
|
||||
const node = nodes[index];
|
||||
result += serializeOuter(node, renderOpts);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
33
backend/node_modules/cheerio/src/slim.ts
generated
vendored
Normal file
33
backend/node_modules/cheerio/src/slim.ts
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @file Alternative entry point for Cheerio that always uses htmlparser2. This
|
||||
* way, parse5 won't be loaded, saving some memory.
|
||||
*/
|
||||
import { type CheerioAPI, getLoad } from './load.js';
|
||||
import { type CheerioOptions } from './options.js';
|
||||
import { getParse } from './parse.js';
|
||||
import type { AnyNode } from 'domhandler';
|
||||
import render from 'dom-serializer';
|
||||
import { parseDocument } from 'htmlparser2';
|
||||
|
||||
export { contains, merge } from './static.js';
|
||||
export type * from './types.js';
|
||||
export type { Cheerio } from './cheerio.js';
|
||||
export type { CheerioOptions, HTMLParser2Options } from './options.js';
|
||||
export type { CheerioAPI } from './load.js';
|
||||
|
||||
/**
|
||||
* Create a querying function, bound to a document created from the provided
|
||||
* markup.
|
||||
*
|
||||
* @param content - Markup to be loaded.
|
||||
* @param options - Options for the created instance.
|
||||
* @param isDocument - Always `false` here, as we are always using
|
||||
* `htmlparser2`.
|
||||
* @returns The loaded document.
|
||||
* @see {@link https://cheerio.js.org#loading} for additional usage information.
|
||||
*/
|
||||
export const load: (
|
||||
content: string | AnyNode | AnyNode[] | Buffer,
|
||||
options?: CheerioOptions | null,
|
||||
isDocument?: boolean,
|
||||
) => CheerioAPI = getLoad(getParse(parseDocument), render);
|
||||
312
backend/node_modules/cheerio/src/static.ts
generated
vendored
Normal file
312
backend/node_modules/cheerio/src/static.ts
generated
vendored
Normal file
@@ -0,0 +1,312 @@
|
||||
import type { BasicAcceptedElems } from './types.js';
|
||||
import type { CheerioAPI } from './load.js';
|
||||
import type { Cheerio } from './cheerio.js';
|
||||
import type { AnyNode, Document } from 'domhandler';
|
||||
import { textContent } from 'domutils';
|
||||
import {
|
||||
type InternalOptions,
|
||||
type CheerioOptions,
|
||||
flattenOptions as flattenOptions,
|
||||
} from './options.js';
|
||||
import type { ExtractedMap, ExtractMap } from './api/extract.js';
|
||||
|
||||
/**
|
||||
* Helper function to render a DOM.
|
||||
*
|
||||
* @param that - Cheerio instance to render.
|
||||
* @param dom - The DOM to render. Defaults to `that`'s root.
|
||||
* @param options - Options for rendering.
|
||||
* @returns The rendered document.
|
||||
*/
|
||||
function render(
|
||||
that: CheerioAPI,
|
||||
dom: BasicAcceptedElems<AnyNode> | undefined,
|
||||
options: InternalOptions,
|
||||
): string {
|
||||
if (!that) return '';
|
||||
|
||||
return that(dom ?? that._root.children, null, undefined, options).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a passed object is an options object.
|
||||
*
|
||||
* @param dom - Object to check if it is an options object.
|
||||
* @param options - Options object.
|
||||
* @returns Whether the object is an options object.
|
||||
*/
|
||||
function isOptions(
|
||||
dom?: BasicAcceptedElems<AnyNode> | CheerioOptions | null,
|
||||
options?: CheerioOptions,
|
||||
): dom is CheerioOptions {
|
||||
return (
|
||||
!options &&
|
||||
typeof dom === 'object' &&
|
||||
dom != null &&
|
||||
!('length' in dom) &&
|
||||
!('type' in dom)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the document.
|
||||
*
|
||||
* @category Static
|
||||
* @param options - Options for the renderer.
|
||||
* @returns The rendered document.
|
||||
*/
|
||||
export function html(this: CheerioAPI, options?: CheerioOptions): string;
|
||||
/**
|
||||
* Renders the document.
|
||||
*
|
||||
* @category Static
|
||||
* @param dom - Element to render.
|
||||
* @param options - Options for the renderer.
|
||||
* @returns The rendered document.
|
||||
*/
|
||||
export function html(
|
||||
this: CheerioAPI,
|
||||
dom?: BasicAcceptedElems<AnyNode>,
|
||||
options?: CheerioOptions,
|
||||
): string;
|
||||
export function html(
|
||||
this: CheerioAPI,
|
||||
dom?: BasicAcceptedElems<AnyNode> | CheerioOptions,
|
||||
options?: CheerioOptions,
|
||||
): string {
|
||||
/*
|
||||
* Be flexible about parameters, sometimes we call html(),
|
||||
* with options as only parameter
|
||||
* check dom argument for dom element specific properties
|
||||
* assume there is no 'length' or 'type' properties in the options object
|
||||
*/
|
||||
const toRender = isOptions(dom) ? ((options = dom), undefined) : dom;
|
||||
|
||||
/*
|
||||
* Sometimes `$.html()` is used without preloading html,
|
||||
* so fallback non-existing options to the default ones.
|
||||
*/
|
||||
const opts = {
|
||||
...this?._options,
|
||||
...flattenOptions(options),
|
||||
};
|
||||
|
||||
return render(this, toRender, opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the document as XML.
|
||||
*
|
||||
* @category Static
|
||||
* @param dom - Element to render.
|
||||
* @returns THe rendered document.
|
||||
*/
|
||||
export function xml(
|
||||
this: CheerioAPI,
|
||||
dom?: BasicAcceptedElems<AnyNode>,
|
||||
): string {
|
||||
const options = { ...this._options, xmlMode: true };
|
||||
|
||||
return render(this, dom, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the document as text.
|
||||
*
|
||||
* This returns the `textContent` of the passed elements. The result will
|
||||
* include the contents of `<script>` and `<style>` elements. To avoid this, use
|
||||
* `.prop('innerText')` instead.
|
||||
*
|
||||
* @category Static
|
||||
* @param elements - Elements to render.
|
||||
* @returns The rendered document.
|
||||
*/
|
||||
export function text(
|
||||
this: CheerioAPI | void,
|
||||
elements?: ArrayLike<AnyNode>,
|
||||
): string {
|
||||
const elems = elements ?? (this ? this.root() : []);
|
||||
|
||||
let ret = '';
|
||||
|
||||
for (let i = 0; i < elems.length; i++) {
|
||||
ret += textContent(elems[i]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string into an array of DOM nodes. The `context` argument has no
|
||||
* meaning for Cheerio, but it is maintained for API compatibility with jQuery.
|
||||
*
|
||||
* @category Static
|
||||
* @param data - Markup that will be parsed.
|
||||
* @param context - Will be ignored. If it is a boolean it will be used as the
|
||||
* value of `keepScripts`.
|
||||
* @param keepScripts - If false all scripts will be removed.
|
||||
* @returns The parsed DOM.
|
||||
* @alias Cheerio.parseHTML
|
||||
* @see {@link https://api.jquery.com/jQuery.parseHTML/}
|
||||
*/
|
||||
export function parseHTML(
|
||||
this: CheerioAPI,
|
||||
data: string,
|
||||
context?: unknown,
|
||||
keepScripts?: boolean,
|
||||
): AnyNode[];
|
||||
export function parseHTML(this: CheerioAPI, data?: '' | null): null;
|
||||
export function parseHTML(
|
||||
this: CheerioAPI,
|
||||
data?: string | null,
|
||||
context?: unknown,
|
||||
keepScripts = typeof context === 'boolean' ? context : false,
|
||||
): AnyNode[] | null {
|
||||
if (!data || typeof data !== 'string') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (typeof context === 'boolean') {
|
||||
keepScripts = context;
|
||||
}
|
||||
|
||||
const parsed = this.load(data, this._options, false);
|
||||
if (!keepScripts) {
|
||||
parsed('script').remove();
|
||||
}
|
||||
|
||||
/*
|
||||
* The `children` array is used by Cheerio internally to group elements that
|
||||
* share the same parents. When nodes created through `parseHTML` are
|
||||
* inserted into previously-existing DOM structures, they will be removed
|
||||
* from the `children` array. The results of `parseHTML` should remain
|
||||
* constant across these operations, so a shallow copy should be returned.
|
||||
*/
|
||||
return [...parsed.root()[0].children];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sometimes you need to work with the top-level root element. To query it, you
|
||||
* can use `$.root()`.
|
||||
*
|
||||
* @category Static
|
||||
* @example
|
||||
*
|
||||
* ```js
|
||||
* $.root().append('<ul id="vegetables"></ul>').html();
|
||||
* //=> <ul id="fruits">...</ul><ul id="vegetables"></ul>
|
||||
* ```
|
||||
*
|
||||
* @returns Cheerio instance wrapping the root node.
|
||||
* @alias Cheerio.root
|
||||
*/
|
||||
export function root(this: CheerioAPI): Cheerio<Document> {
|
||||
return this(this._root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the `contained` DOM element is a descendant of the
|
||||
* `container` DOM element.
|
||||
*
|
||||
* @category Static
|
||||
* @param container - Potential parent node.
|
||||
* @param contained - Potential child node.
|
||||
* @returns Indicates if the nodes contain one another.
|
||||
* @alias Cheerio.contains
|
||||
* @see {@link https://api.jquery.com/jQuery.contains/}
|
||||
*/
|
||||
export function contains(container: AnyNode, contained: AnyNode): boolean {
|
||||
// According to the jQuery API, an element does not "contain" itself
|
||||
if (contained === container) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Step up the descendants, stopping when the root element is reached
|
||||
* (signaled by `.parent` returning a reference to the same object)
|
||||
*/
|
||||
let next: AnyNode | null = contained;
|
||||
while (next && next !== next.parent) {
|
||||
next = next.parent;
|
||||
if (next === container) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract multiple values from a document, and store them in an object.
|
||||
*
|
||||
* @category Static
|
||||
* @param map - An object containing key-value pairs. The keys are the names of
|
||||
* the properties to be created on the object, and the values are the
|
||||
* selectors to be used to extract the values.
|
||||
* @returns An object containing the extracted values.
|
||||
*/
|
||||
export function extract<M extends ExtractMap>(
|
||||
this: CheerioAPI,
|
||||
map: M,
|
||||
): ExtractedMap<M> {
|
||||
return this.root().extract(map);
|
||||
}
|
||||
|
||||
type Writable<T> = { -readonly [P in keyof T]: T[P] };
|
||||
|
||||
/**
|
||||
* $.merge().
|
||||
*
|
||||
* @category Static
|
||||
* @param arr1 - First array.
|
||||
* @param arr2 - Second array.
|
||||
* @returns `arr1`, with elements of `arr2` inserted.
|
||||
* @alias Cheerio.merge
|
||||
* @see {@link https://api.jquery.com/jQuery.merge/}
|
||||
*/
|
||||
export function merge<T>(
|
||||
arr1: Writable<ArrayLike<T>>,
|
||||
arr2: ArrayLike<T>,
|
||||
): ArrayLike<T> | undefined {
|
||||
if (!isArrayLike(arr1) || !isArrayLike(arr2)) {
|
||||
return;
|
||||
}
|
||||
let newLength = arr1.length;
|
||||
const len = +arr2.length;
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
arr1[newLength++] = arr2[i];
|
||||
}
|
||||
arr1.length = newLength;
|
||||
return arr1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an object is array-like.
|
||||
*
|
||||
* @category Static
|
||||
* @param item - Item to check.
|
||||
* @returns Indicates if the item is array-like.
|
||||
*/
|
||||
function isArrayLike(item: unknown): item is ArrayLike<unknown> {
|
||||
if (Array.isArray(item)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
typeof item !== 'object' ||
|
||||
item === null ||
|
||||
!('length' in item) ||
|
||||
typeof item.length !== 'number' ||
|
||||
item.length < 0
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < item.length; i++) {
|
||||
if (!(i in item)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
58
backend/node_modules/cheerio/src/types.ts
generated
vendored
Normal file
58
backend/node_modules/cheerio/src/types.ts
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
/** @file Types used in signatures of Cheerio methods. */
|
||||
|
||||
type LowercaseLetters =
|
||||
| 'a'
|
||||
| 'b'
|
||||
| 'c'
|
||||
| 'd'
|
||||
| 'e'
|
||||
| 'f'
|
||||
| 'g'
|
||||
| 'h'
|
||||
| 'i'
|
||||
| 'j'
|
||||
| 'k'
|
||||
| 'l'
|
||||
| 'm'
|
||||
| 'n'
|
||||
| 'o'
|
||||
| 'p'
|
||||
| 'q'
|
||||
| 'r'
|
||||
| 's'
|
||||
| 't'
|
||||
| 'u'
|
||||
| 'v'
|
||||
| 'w'
|
||||
| 'x'
|
||||
| 'y'
|
||||
| 'z';
|
||||
|
||||
type AlphaNumeric =
|
||||
| LowercaseLetters
|
||||
| Uppercase<LowercaseLetters>
|
||||
| `${number}`;
|
||||
|
||||
type SelectorSpecial = '.' | '#' | ':' | '|' | '>' | '+' | '~' | '[';
|
||||
/**
|
||||
* Type for identifying selectors. Allows us to "upgrade" queries using
|
||||
* selectors to return `Element`s.
|
||||
*/
|
||||
export type SelectorType =
|
||||
| `${SelectorSpecial}${AlphaNumeric}${string}`
|
||||
| `${AlphaNumeric}${string}`;
|
||||
|
||||
import type { Cheerio } from './cheerio.js';
|
||||
import type { AnyNode } from 'domhandler';
|
||||
|
||||
/** Elements that can be passed to manipulation methods. */
|
||||
export type BasicAcceptedElems<T extends AnyNode> = ArrayLike<T> | T | string;
|
||||
/** Elements that can be passed to manipulation methods, including functions. */
|
||||
export type AcceptedElems<T extends AnyNode> =
|
||||
| BasicAcceptedElems<T>
|
||||
| ((this: T, i: number, el: T) => BasicAcceptedElems<T>);
|
||||
|
||||
/** Function signature, for traversal methods. */
|
||||
export type FilterFunction<T> = (this: T, i: number, el: T) => boolean;
|
||||
/** Supported filter types, for traversal methods. */
|
||||
export type AcceptedFilters<T> = string | FilterFunction<T> | T | Cheerio<T>;
|
||||
99
backend/node_modules/cheerio/src/utils.ts
generated
vendored
Normal file
99
backend/node_modules/cheerio/src/utils.ts
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
import type { AnyNode } from 'domhandler';
|
||||
import type { Cheerio } from './cheerio.js';
|
||||
|
||||
/**
|
||||
* Checks if an object is a Cheerio instance.
|
||||
*
|
||||
* @category Utils
|
||||
* @param maybeCheerio - The object to check.
|
||||
* @returns Whether the object is a Cheerio instance.
|
||||
*/
|
||||
export function isCheerio<T>(
|
||||
maybeCheerio: unknown,
|
||||
): maybeCheerio is Cheerio<T> {
|
||||
return (maybeCheerio as Cheerio<T>).cheerio != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string to camel case notation.
|
||||
*
|
||||
* @private
|
||||
* @category Utils
|
||||
* @param str - The string to be converted.
|
||||
* @returns String in camel case notation.
|
||||
*/
|
||||
export function camelCase(str: string): string {
|
||||
return str.replace(/[._-](\w|$)/g, (_, x) => (x as string).toUpperCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string from camel case to "CSS case", where word boundaries are
|
||||
* described by hyphens ("-") and all characters are lower-case.
|
||||
*
|
||||
* @private
|
||||
* @category Utils
|
||||
* @param str - The string to be converted.
|
||||
* @returns String in "CSS case".
|
||||
*/
|
||||
export function cssCase(str: string): string {
|
||||
return str.replace(/[A-Z]/g, '-$&').toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over each DOM element without creating intermediary Cheerio
|
||||
* instances.
|
||||
*
|
||||
* This is indented for use internally to avoid otherwise unnecessary memory
|
||||
* pressure introduced by _make.
|
||||
*
|
||||
* @category Utils
|
||||
* @param array - The array to iterate over.
|
||||
* @param fn - Function to call.
|
||||
* @returns The original instance.
|
||||
*/
|
||||
export function domEach<
|
||||
T extends AnyNode,
|
||||
Arr extends ArrayLike<T> = Cheerio<T>,
|
||||
>(array: Arr, fn: (elem: T, index: number) => void): Arr {
|
||||
const len = array.length;
|
||||
for (let i = 0; i < len; i++) fn(array[i], i);
|
||||
return array;
|
||||
}
|
||||
|
||||
const enum CharacterCode {
|
||||
LowerA = 97,
|
||||
LowerZ = 122,
|
||||
UpperA = 65,
|
||||
UpperZ = 90,
|
||||
Exclamation = 33,
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if string is HTML.
|
||||
*
|
||||
* Tests for a `<` within a string, immediate followed by a letter and
|
||||
* eventually followed by a `>`.
|
||||
*
|
||||
* @private
|
||||
* @category Utils
|
||||
* @param str - The string to check.
|
||||
* @returns Indicates if `str` is HTML.
|
||||
*/
|
||||
export function isHtml(str: string): boolean {
|
||||
if (typeof str !== 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const tagStart = str.indexOf('<');
|
||||
|
||||
if (tagStart === -1 || tagStart > str.length - 3) return false;
|
||||
|
||||
const tagChar = str.charCodeAt(tagStart + 1) as CharacterCode;
|
||||
|
||||
return (
|
||||
((tagChar >= CharacterCode.LowerA && tagChar <= CharacterCode.LowerZ) ||
|
||||
(tagChar >= CharacterCode.UpperA && tagChar <= CharacterCode.UpperZ) ||
|
||||
tagChar === CharacterCode.Exclamation) &&
|
||||
str.includes('>', tagStart + 2)
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user