rusteerio 1.0.1 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +42 -1
- package/Cargo.toml +8 -1
- package/README.md +90 -51
- package/THIRD_PARTY_NOTICES.md +107 -2
- package/dist/{chunk-DMRD3FGC.mjs → chunk-FPRP6UIX.mjs} +2233 -751
- package/dist/chunk-FPRP6UIX.mjs.map +1 -0
- package/dist/index.cjs +2232 -750
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/slim.cjs +2226 -745
- package/dist/slim.cjs.map +1 -1
- package/dist/slim.d.mts +2 -2
- package/dist/slim.d.ts +2 -2
- package/dist/slim.mjs +1 -1
- package/dist/{types-BP6TpxE5.d.mts → types-Cs9UP1Vv.d.mts} +3 -3
- package/dist/{types-BP6TpxE5.d.ts → types-Cs9UP1Vv.d.ts} +3 -3
- package/dist/utils.d.mts +1 -1
- package/dist/utils.d.ts +1 -1
- package/native/src/arena.rs +219 -80
- package/native/src/attributes.rs +11 -5
- package/native/src/document_order.rs +711 -0
- package/native/src/extraction.rs +15 -3
- package/native/src/gc.rs +12 -3
- package/native/src/id_hash.rs +42 -0
- package/native/src/immutable.rs +256 -0
- package/native/src/lib.rs +1039 -171
- package/native/src/runtime.rs +189 -33
- package/native/src/selector.rs +346 -44
- package/native/src/sink.rs +14 -1
- package/native/src/snapshot.rs +527 -0
- package/native/src/stream.rs +2 -2
- package/native/src/tokenizer.rs +29 -12
- package/native/vendor/html5ever-0.35.0/src/tokenizer/char_ref/mod.rs +79 -0
- package/package.json +3 -1
- package/rusteerio.darwin-arm64.node +0 -0
- package/rusteerio.darwin-x64.node +0 -0
- package/rusteerio.linux-arm64-gnu.node +0 -0
- package/rusteerio.linux-arm64-musl.node +0 -0
- package/rusteerio.linux-x64-gnu.node +0 -0
- package/rusteerio.linux-x64-musl.node +0 -0
- package/rusteerio.win32-arm64-msvc.node +0 -0
- package/rusteerio.win32-x64-msvc.node +0 -0
- package/src/attributes.ts +78 -44
- package/src/codec.ts +22 -3
- package/src/extract.ts +86 -2
- package/src/hooks.ts +148 -2
- package/src/index.ts +51 -40
- package/src/match.ts +207 -0
- package/src/native.ts +9 -1
- package/src/nodes.ts +531 -183
- package/src/router.ts +792 -229
- package/src/selection.ts +239 -125
- package/src/snapshot.ts +255 -0
- package/src/transport.ts +51 -42
- package/dist/chunk-DMRD3FGC.mjs.map +0 -1
package/src/selection.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { AnyNode, Document, Element, ParentNode, CheerioAPI, CheerioOptions, AcceptedFilters, BasicAcceptedElems, AcceptedElems, ExtractMap, ExtractedMap } from './types.js';
|
|
2
2
|
import { requireNative, type NativeDocument } from './native.js';
|
|
3
|
-
import { getNode, importNodes, nodeBinding, nodeId, nodeRecord, operation, isTag } from './nodes.js';
|
|
3
|
+
import { getNode, importNodes, nodeBinding, nodeId, nodeRecord, operation, isTag, traverseRecords, viewId } from './nodes.js';
|
|
4
4
|
import { installAttributes, type AttributeMethods } from './attributes.js';
|
|
5
5
|
import { extract } from './extract.js';
|
|
6
|
-
import { sameNodeSpace,
|
|
6
|
+
import { API, DOCUMENT, exposeDocumentTo, IDS, IS_PLAIN, LEASE, leaseNodes, routeDocument, sameNodeSpace, updateLease, VALUES, type Lease } from './router.js';
|
|
7
7
|
import { parse } from './parse.js';
|
|
8
|
+
import { matchSimpleSelector, selectSimpleWithin } from './match.js';
|
|
8
9
|
const numeric = /^(?:0|[1-9]\d*)$/;
|
|
9
10
|
const isMarkup = (value: string) => /<[a-zA-Z!/?]/.test(value);
|
|
10
11
|
function* customPseudoArguments(selector: string, name: string): Generator<string | undefined> {
|
|
@@ -75,137 +76,199 @@ function* customPseudoArguments(selector: string, name: string): Generator<strin
|
|
|
75
76
|
index = end;
|
|
76
77
|
}
|
|
77
78
|
}
|
|
78
|
-
|
|
79
|
+
const isIndex = (property: string | symbol): boolean => {
|
|
80
|
+
if (typeof property !== 'string') return false;
|
|
81
|
+
const first = property.charCodeAt(0);
|
|
82
|
+
return first >= 48 && first <= 57 && numeric.test(property);
|
|
83
|
+
};
|
|
84
|
+
/** Results up to this size are plain objects holding their nodes, like Cheerio's own selections. */
|
|
85
|
+
const plainSelectionLimit = 16;
|
|
86
|
+
const lengthOf = (target: Cheerio<any>): number => target[VALUES]?.length ?? target[IDS].length;
|
|
87
|
+
function nodeAt(target: Cheerio<any>, index: number): any {
|
|
88
|
+
if (target[VALUES]) return target[VALUES][index];
|
|
89
|
+
const id = target[IDS][index];
|
|
90
|
+
return id === undefined ? undefined : getNode(target[DOCUMENT], id);
|
|
91
|
+
}
|
|
92
|
+
function releaseIds(target: Cheerio<any>): void {
|
|
93
|
+
if (target[LEASE]) updateLease(target[DOCUMENT], target[LEASE], target[VALUES] ? [] : target[IDS]);
|
|
94
|
+
}
|
|
95
|
+
function writeIndex(target: Cheerio<any>, index: number, value: any): void {
|
|
96
|
+
if (target[VALUES]) target[VALUES][index] = value;
|
|
97
|
+
else {
|
|
98
|
+
const id = importNodes(target[DOCUMENT], [value])[0];
|
|
99
|
+
if (id === undefined) {
|
|
100
|
+
target[VALUES] = target[IDS].map((existing) => getNode(target[DOCUMENT], existing));
|
|
101
|
+
target[VALUES][index] = value;
|
|
102
|
+
}
|
|
103
|
+
else target[IDS][index] = id;
|
|
104
|
+
}
|
|
105
|
+
releaseIds(target);
|
|
106
|
+
}
|
|
107
|
+
/** Large selections resolve nodes only when an index is read. */
|
|
108
|
+
const selectionHandler: ProxyHandler<Cheerio<any>> = {
|
|
109
|
+
get: (target, property) => isIndex(property) ? nodeAt(target, Number(property)) : property === 'length' ? lengthOf(target) : Reflect.get(target, property),
|
|
110
|
+
set: (target, property, value) => {
|
|
111
|
+
if (isIndex(property)) { writeIndex(target, Number(property), value); return true; }
|
|
112
|
+
if (property === 'length') {
|
|
113
|
+
if (target[VALUES]) target[VALUES].length = value;
|
|
114
|
+
else target[IDS].length = value;
|
|
115
|
+
releaseIds(target);
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
return Reflect.set(target, property, value);
|
|
119
|
+
},
|
|
120
|
+
has: (target, property) => isIndex(property) ? Number(property) < lengthOf(target) : property === 'length' || Reflect.has(target, property),
|
|
121
|
+
ownKeys: (target) => [...Array.from({ length: lengthOf(target) }, (_, i) => String(i)), 'length', ...Reflect.ownKeys(target)],
|
|
122
|
+
getOwnPropertyDescriptor: (target, property) => {
|
|
123
|
+
if (isIndex(property) && Number(property) < lengthOf(target))
|
|
124
|
+
return { value: nodeAt(target, Number(property)), writable: true, configurable: true, enumerable: true };
|
|
125
|
+
if (property === 'length') return { value: lengthOf(target), writable: true, configurable: true, enumerable: true };
|
|
126
|
+
return Reflect.getOwnPropertyDescriptor(target, property);
|
|
127
|
+
},
|
|
128
|
+
deleteProperty: (target, property) => {
|
|
129
|
+
if (isIndex(property)) {
|
|
130
|
+
if (target[VALUES])
|
|
131
|
+
delete target[VALUES][Number(property)];
|
|
132
|
+
else
|
|
133
|
+
delete target[IDS][Number(property)];
|
|
134
|
+
releaseIds(target);
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
return Reflect.deleteProperty(target, property);
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
/** An array-like selection of native nodes. */
|
|
79
141
|
export class Cheerio<T = AnyNode> implements ArrayLike<T>, Iterable<T> {
|
|
80
142
|
[index: number]: T;
|
|
81
|
-
/** @internal */ declare
|
|
82
|
-
/** @internal */ declare
|
|
83
|
-
/** @internal */ declare
|
|
84
|
-
/** @internal */ declare
|
|
85
|
-
/** @internal */ declare
|
|
143
|
+
/** @internal */ declare [DOCUMENT]: NativeDocument;
|
|
144
|
+
/** @internal */ declare [API]: CheerioAPI;
|
|
145
|
+
/** @internal */ declare [IDS]: number[];
|
|
146
|
+
/** @internal */ declare [VALUES]: T[] | undefined;
|
|
147
|
+
/** @internal */ declare [LEASE]: Lease | undefined;
|
|
148
|
+
/** @internal Whether the selection holds its items as own index properties, as Cheerio's selections do. */ declare [IS_PLAIN]: boolean;
|
|
149
|
+
/** @internal */ get _document(): NativeDocument { return this[DOCUMENT]; }
|
|
150
|
+
/** @internal */ get _api(): CheerioAPI { return this[API]; }
|
|
151
|
+
/** @internal */ get _ids(): number[] { return this[IDS]; }
|
|
152
|
+
declare length: number;
|
|
86
153
|
declare options: CheerioOptions;
|
|
87
154
|
declare _root: Cheerio<Document> | null;
|
|
88
155
|
declare prevObject: Cheerio<any> | undefined;
|
|
89
|
-
declare
|
|
90
|
-
cheerio = '[cheerio object]' as const;
|
|
156
|
+
declare cheerio: '[cheerio object]';
|
|
91
157
|
constructor(api: CheerioAPI, document: NativeDocument, ids: number[] = [], root: Cheerio<Document> | null = null, options: CheerioOptions = {}, values?: T[]) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if (target._values)
|
|
113
|
-
target._values[Number(property)] = value;
|
|
114
|
-
else {
|
|
115
|
-
const id = importNodes(document, [value])[0];
|
|
116
|
-
if (id === undefined) {
|
|
117
|
-
target._values = target._ids.map((existing) => getNode(document, existing)) as T[];
|
|
118
|
-
target._values[Number(property)] = value;
|
|
119
|
-
}
|
|
120
|
-
else
|
|
121
|
-
target._ids[Number(property)] = id;
|
|
122
|
-
}
|
|
123
|
-
target._lease = retainNodes(document, target._values ? [] : target._ids);
|
|
124
|
-
return true;
|
|
125
|
-
}
|
|
126
|
-
return Reflect.set(target, property, value, receiver);
|
|
127
|
-
},
|
|
128
|
-
has: (target, property) => typeof property === 'string' && numeric.test(property) ? Number(property) < target.length : Reflect.has(target, property),
|
|
129
|
-
ownKeys: (target) => [...Array.from({ length: target.length }, (_, i) => String(i)), ...Reflect.ownKeys(target)],
|
|
130
|
-
getOwnPropertyDescriptor: (target, property) => typeof property === 'string' && numeric.test(property) && Number(property) < target.length
|
|
131
|
-
? { value: target._values ? target._values[Number(property)] : getNode(document, target._ids[Number(property)]), writable: true, configurable: true, enumerable: true }
|
|
132
|
-
: Reflect.getOwnPropertyDescriptor(target, property),
|
|
133
|
-
deleteProperty: (target, property) => {
|
|
134
|
-
if (typeof property === 'string' && numeric.test(property)) {
|
|
135
|
-
if (target._values)
|
|
136
|
-
delete target._values[Number(property)];
|
|
137
|
-
else
|
|
138
|
-
delete target._ids[Number(property)];
|
|
139
|
-
target._lease = retainNodes(document, target._values ? [] : target._ids);
|
|
140
|
-
return true;
|
|
141
|
-
}
|
|
142
|
-
return Reflect.deleteProperty(target, property);
|
|
143
|
-
},
|
|
144
|
-
});
|
|
158
|
+
this[DOCUMENT] = document;
|
|
159
|
+
this[API] = api;
|
|
160
|
+
this[IDS] = ids;
|
|
161
|
+
this[VALUES] = values;
|
|
162
|
+
this[LEASE] = undefined;
|
|
163
|
+
const count = values ? values.length : ids.length;
|
|
164
|
+
const isPlain = count <= plainSelectionLimit;
|
|
165
|
+
this[IS_PLAIN] = isPlain;
|
|
166
|
+
// Own keys enumerate in Cheerio's order: length, options, _root.
|
|
167
|
+
if (isPlain) this.length = count;
|
|
168
|
+
this.options = options;
|
|
169
|
+
this._root = root;
|
|
170
|
+
if (isPlain) {
|
|
171
|
+
// Each node object keeps its own node alive, so a plain selection needs no lease.
|
|
172
|
+
if (values) for (let index = 0; index < count; index++) this[index] = values[index];
|
|
173
|
+
else for (let index = 0; index < count; index++) this[index] = getNode(document, ids[index]) as T;
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (!values) this[LEASE] = leaseNodes(document, this, ids);
|
|
177
|
+
return new Proxy(this, selectionHandler);
|
|
145
178
|
}
|
|
146
|
-
/** @internal
|
|
147
|
-
|
|
179
|
+
/** @internal Re-reads a plain selection after direct index writes or after its nodes moved documents. */
|
|
180
|
+
_syncIndices(): void {
|
|
181
|
+
const length = this.length;
|
|
182
|
+
const values = this[VALUES];
|
|
183
|
+
const ids = this[IDS];
|
|
184
|
+
let isChanged = (values ?? ids).length !== length;
|
|
185
|
+
for (let index = 0; !isChanged && index < length; index++) isChanged = values ? this[index] !== values[index] : viewId(this[index]) !== ids[index];
|
|
186
|
+
if (!isChanged) return;
|
|
187
|
+
const items: T[] = Array.from({ length }, (_unused, index) => this[index]);
|
|
188
|
+
const isAllNodes = items.every((value: any) => value && typeof value === 'object' && typeof value.type === 'string');
|
|
189
|
+
this[VALUES] = isAllNodes ? undefined : items;
|
|
190
|
+
this[IDS] = isAllNodes ? importNodes(this[DOCUMENT], items) : [];
|
|
191
|
+
}
|
|
192
|
+
/** @internal */ get ids(): number[] {
|
|
193
|
+
if (this[IS_PLAIN]) this._syncIndices();
|
|
194
|
+
return this[VALUES] ? importNodes(this[DOCUMENT], this[VALUES]) : this[IDS];
|
|
195
|
+
}
|
|
196
|
+
/** @internal */ op<R = any>(command: string, args: unknown = {}): R { const values = args as Record<string, unknown>; return operation<R>(this[DOCUMENT], command, { ...values, options: values.options ?? this.options }); }
|
|
148
197
|
/** @internal */ _query(command: 'select' | 'matches', args: {
|
|
149
198
|
selector: string;
|
|
150
199
|
ids?: number[];
|
|
151
200
|
contexts?: number[];
|
|
152
201
|
options?: CheerioOptions;
|
|
153
202
|
}): number[] {
|
|
154
|
-
const
|
|
203
|
+
const pseudos = this.options.pseudos;
|
|
204
|
+
const callbacks = pseudos ? Object.entries(pseudos).filter((entry): entry is [
|
|
155
205
|
string,
|
|
156
206
|
(element: Element, argument?: string | null) => boolean
|
|
157
|
-
] => typeof entry[1] === 'function');
|
|
158
|
-
if (!callbacks.length)
|
|
159
|
-
|
|
207
|
+
] => typeof entry[1] === 'function') : [];
|
|
208
|
+
if (!callbacks.length) {
|
|
209
|
+
if (command === 'matches' && args.ids) {
|
|
210
|
+
const matched = matchSimpleSelector(this[DOCUMENT], args.ids, args.selector, args.options ?? this.options);
|
|
211
|
+
if (matched) return matched;
|
|
212
|
+
}
|
|
213
|
+
if (command === 'select' && args.contexts) {
|
|
214
|
+
const found = selectSimpleWithin(this[DOCUMENT], args.contexts, args.selector, args.options ?? this.options);
|
|
215
|
+
if (found) return found;
|
|
216
|
+
}
|
|
217
|
+
return command === 'select' && !args.options ? this[DOCUMENT].select(args.selector, args.contexts) : this.op(command, args);
|
|
218
|
+
}
|
|
160
219
|
const pseudoMatches: Record<string, number[]> = {};
|
|
161
|
-
const candidates = [...new Set([...this.
|
|
220
|
+
const candidates = [...new Set([...this[DOCUMENT].select('*'), ...(args.ids ?? []), ...(args.contexts ? this[DOCUMENT].select('*', args.contexts) : [])])];
|
|
162
221
|
for (const [name, callback] of callbacks) {
|
|
163
222
|
for (const argument of customPseudoArguments(args.selector, name)) {
|
|
164
223
|
if (argument !== undefined && callback.length < 2)
|
|
165
224
|
throw new Error(`Pseudo-class :${name} doesn't have any arguments`);
|
|
166
225
|
if (argument === undefined && callback.length >= 2)
|
|
167
226
|
throw new Error(`Pseudo-class :${name} requires an argument`);
|
|
168
|
-
pseudoMatches[`${name}(${argument ?? ''})`] = candidates.filter((id) => callback(getNode(this
|
|
227
|
+
pseudoMatches[`${name}(${argument ?? ''})`] = candidates.filter((id) => callback(getNode(this[DOCUMENT], id) as Element, argument ?? null));
|
|
169
228
|
}
|
|
170
229
|
}
|
|
171
230
|
return this.op(command, { ...args, pseudoMatches });
|
|
172
231
|
}
|
|
173
232
|
/** @internal */ _fromIds<R = AnyNode>(ids: number[]): Cheerio<R> {
|
|
174
|
-
const result = new (this.
|
|
233
|
+
const result = new (this[API].fn.constructor as typeof Cheerio)<R>(this[API], this[DOCUMENT], ids, this._root, this.options);
|
|
175
234
|
result.prevObject = this;
|
|
176
235
|
return result;
|
|
177
236
|
}
|
|
178
237
|
/** @internal */ _fromValues<R>(values: R[]): Cheerio<R> {
|
|
179
238
|
const allNodes = values.every((value: any) => value && typeof value === 'object' && typeof value.type === 'string');
|
|
180
|
-
const result = new (this.
|
|
239
|
+
const result = new (this[API].fn.constructor as typeof Cheerio)<R>(this[API], this[DOCUMENT], allNodes ? importNodes(this[DOCUMENT], values) : [], this._root, this.options, allNodes ? undefined : values);
|
|
181
240
|
result.prevObject = this;
|
|
182
241
|
return result;
|
|
183
242
|
}
|
|
184
243
|
_make<R>(value: ArrayLike<R> | R | string, context?: BasicAcceptedElems<AnyNode>): Cheerio<R>;
|
|
185
244
|
_make<R>(value: any, context?: any): Cheerio<R> {
|
|
186
|
-
const result = typeof value === 'string' || value?.cheerio ? this
|
|
245
|
+
const result = typeof value === 'string' || value?.cheerio ? this[API](value, context) : this._fromValues<R>(value == null ? [] : typeof value === 'object' && 'length' in value && !value.type ? Array.from(value) : [value]);
|
|
187
246
|
result.prevObject = this;
|
|
188
247
|
return result as Cheerio<R>;
|
|
189
248
|
}
|
|
190
249
|
_parse(content: any, options: CheerioOptions, isDocument: boolean, context: ParentNode | null): Document {
|
|
191
250
|
return parse(content, options, isDocument, context);
|
|
192
251
|
}
|
|
193
|
-
_render(content: any): string { return this.
|
|
252
|
+
_render(content: any): string { return this[DOCUMENT].outerHtml(importNodes(this[DOCUMENT], content)); }
|
|
194
253
|
/** @internal */ _domIds(content: any, contextId?: number): number[] {
|
|
195
254
|
if (content == null || content === false)
|
|
196
255
|
return [];
|
|
197
256
|
if (typeof content === 'string')
|
|
198
257
|
return this.op('create', { html: content, contextId, options: this.options });
|
|
199
|
-
if (content?.cheerio)
|
|
200
|
-
|
|
258
|
+
if (content?.cheerio) {
|
|
259
|
+
if (!content[DOCUMENT] || !sameNodeSpace(this[DOCUMENT], content[DOCUMENT]))
|
|
260
|
+
return importNodes(this[DOCUMENT], content.toArray());
|
|
261
|
+
exposeDocumentTo(this[DOCUMENT], content[DOCUMENT]);
|
|
262
|
+
return content.ids;
|
|
263
|
+
}
|
|
201
264
|
if (typeof content === 'object' && 'length' in content && !content.type)
|
|
202
265
|
return Array.from(content as ArrayLike<any>).flatMap((item) => this._domIds(item, contextId));
|
|
203
|
-
return importNodes(this
|
|
266
|
+
return importNodes(this[DOCUMENT], [content]);
|
|
204
267
|
}
|
|
205
268
|
_makeDomArray<T extends AnyNode>(this: Cheerio<T>, elem?: BasicAcceptedElems<AnyNode> | BasicAcceptedElems<AnyNode>[], clone?: boolean): AnyNode[];
|
|
206
269
|
_makeDomArray(this: Cheerio<any>, content: any, clone: any = false): any {
|
|
207
270
|
const ids = this._domIds(content);
|
|
208
|
-
return (clone ? this.op<number[]>('clone', { ids, deep: true }) : ids).map((id) => getNode(this
|
|
271
|
+
return (clone ? this.op<number[]>('clone', { ids, deep: true }) : ids).map((id) => getNode(this[DOCUMENT], id));
|
|
209
272
|
}
|
|
210
273
|
find<T extends AnyNode>(this: Cheerio<T>, selectorOrHaystack?: string | Cheerio<Element> | Element): Cheerio<Element>;
|
|
211
274
|
find(this: Cheerio<any>, selector?: any): any {
|
|
@@ -213,7 +276,7 @@ export class Cheerio<T = AnyNode> implements ArrayLike<T>, Iterable<T> {
|
|
|
213
276
|
return this._fromIds([]);
|
|
214
277
|
if (typeof selector === 'string')
|
|
215
278
|
return this._findBySelector(selector, Infinity);
|
|
216
|
-
const ids = importNodes(this
|
|
279
|
+
const ids = importNodes(this[DOCUMENT], selector);
|
|
217
280
|
return this._fromIds(ids.filter((id) => this.ids.some((container) => this.op<boolean>('contains', { container, contained: id }))));
|
|
218
281
|
}
|
|
219
282
|
_findBySelector<T extends AnyNode>(this: Cheerio<T>, selector: string, limit: number): Cheerio<Element>;
|
|
@@ -226,19 +289,19 @@ export class Cheerio<T = AnyNode> implements ArrayLike<T>, Iterable<T> {
|
|
|
226
289
|
return ids;
|
|
227
290
|
if (typeof selector === 'string')
|
|
228
291
|
return this._query('matches', { ids, selector, options: this.options });
|
|
229
|
-
return ids.filter((id, index) => this._predicate(selector, getNode(this
|
|
292
|
+
return ids.filter((id, index) => this._predicate(selector, getNode(this[DOCUMENT], id), index));
|
|
230
293
|
}
|
|
231
294
|
/** @internal */ _predicate(selector: any, node: any, index: number): boolean {
|
|
232
295
|
if (typeof selector === 'function')
|
|
233
296
|
return !!selector.call(node, index, node);
|
|
234
297
|
if (typeof selector === 'string')
|
|
235
|
-
return this._query('matches', { ids: importNodes(this
|
|
298
|
+
return this._query('matches', { ids: importNodes(this[DOCUMENT], [node]), selector, options: this.options }).length > 0;
|
|
236
299
|
if (selector?.cheerio)
|
|
237
300
|
return selector.toArray().includes(node);
|
|
238
301
|
return node === selector;
|
|
239
302
|
}
|
|
240
303
|
/** @internal */ _traverse(kind: string, selector?: any): Cheerio<Element> {
|
|
241
|
-
let ids = this.op<number[]>('traverse', { ids: this.ids, kind });
|
|
304
|
+
let ids = traverseRecords(this[DOCUMENT], this.ids, kind) ?? this.op<number[]>('traverse', { ids: this.ids, kind });
|
|
242
305
|
ids = this._filterIds(ids, selector);
|
|
243
306
|
if (this.length > 1 && ids.length > 1) {
|
|
244
307
|
if (kind === 'parents' || kind === 'siblings')
|
|
@@ -271,8 +334,8 @@ export class Cheerio<T = AnyNode> implements ArrayLike<T>, Iterable<T> {
|
|
|
271
334
|
/** @internal */ _until(kind: string, until?: any, filter?: any): Cheerio<Element> {
|
|
272
335
|
let ids: number[] = [];
|
|
273
336
|
for (const id of this.ids)
|
|
274
|
-
for (const candidate of this.op<number[]>('traverse', { ids: [id], kind })) {
|
|
275
|
-
if (until && this._predicate(until, getNode(this
|
|
337
|
+
for (const candidate of traverseRecords(this[DOCUMENT], [id], kind) ?? this.op<number[]>('traverse', { ids: [id], kind })) {
|
|
338
|
+
if (until && this._predicate(until, getNode(this[DOCUMENT], candidate), ids.length))
|
|
276
339
|
break;
|
|
277
340
|
ids.push(candidate);
|
|
278
341
|
}
|
|
@@ -293,9 +356,11 @@ export class Cheerio<T = AnyNode> implements ArrayLike<T>, Iterable<T> {
|
|
|
293
356
|
return this._fromIds([]);
|
|
294
357
|
const result: number[] = [];
|
|
295
358
|
for (const id of this.ids) {
|
|
296
|
-
const record = nodeRecord(this
|
|
297
|
-
const candidates = [...(['tag', 'script', 'style'].includes(record.type) ? [id] : []), ...this.op<number[]>('traverse', { ids: [id], kind: 'parents' })];
|
|
298
|
-
|
|
359
|
+
const record = nodeRecord(this[DOCUMENT], id);
|
|
360
|
+
const candidates = [...(['tag', 'script', 'style'].includes(record.type) ? [id] : []), ...(traverseRecords(this[DOCUMENT], [id], 'parents') ?? this.op<number[]>('traverse', { ids: [id], kind: 'parents' }))];
|
|
361
|
+
// One native match for all candidates replaces a round trip per ancestor.
|
|
362
|
+
const matched = typeof selector === 'string' ? new Set(this._filterIds(candidates, selector)) : undefined;
|
|
363
|
+
const match = matched ? candidates.find((candidate) => matched.has(candidate)) : candidates.find((candidate) => this._predicate(selector, getNode(this[DOCUMENT], candidate), 0));
|
|
299
364
|
if (match !== undefined && !result.includes(match))
|
|
300
365
|
result.push(match);
|
|
301
366
|
}
|
|
@@ -303,17 +368,29 @@ export class Cheerio<T = AnyNode> implements ArrayLike<T>, Iterable<T> {
|
|
|
303
368
|
}
|
|
304
369
|
each<T>(this: Cheerio<T>, fn: (this: T, i: number, el: T) => void | boolean): Cheerio<T>;
|
|
305
370
|
each(this: Cheerio<any>, callback: any): any {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
371
|
+
if (this[IS_PLAIN]) {
|
|
372
|
+
const length = this.length;
|
|
373
|
+
for (let i = 0; i < length; i++)
|
|
374
|
+
if (callback.call(this[i], i, this[i]) === false)
|
|
375
|
+
break;
|
|
376
|
+
return this;
|
|
377
|
+
}
|
|
378
|
+
// Reading ids directly avoids a string-keyed proxy trap per index.
|
|
379
|
+
const values = this[VALUES], ids = this[IDS], document = this[DOCUMENT];
|
|
380
|
+
const length = values ? values.length : ids.length;
|
|
381
|
+
for (let i = 0; i < length; i++) {
|
|
382
|
+
const node = values ? values[i] : getNode(document, ids[i]);
|
|
383
|
+
if (callback.call(node, i, node) === false)
|
|
309
384
|
break;
|
|
385
|
+
}
|
|
310
386
|
return this;
|
|
311
387
|
}
|
|
312
388
|
map<T, M>(this: Cheerio<T>, fn: (this: T, i: number, el: T) => M[] | M | null | undefined): Cheerio<M>;
|
|
313
389
|
map<R>(this: Cheerio<any>, callback: any): any {
|
|
314
390
|
const result: R[] = [];
|
|
315
|
-
|
|
316
|
-
|
|
391
|
+
const items = this._items();
|
|
392
|
+
for (let i = 0; i < items.length; i++) {
|
|
393
|
+
const value = callback.call(items[i], i, items[i]);
|
|
317
394
|
if (value != null)
|
|
318
395
|
result.push(...(Array.isArray(value) ? value : [value]));
|
|
319
396
|
}
|
|
@@ -329,7 +406,7 @@ export class Cheerio<T = AnyNode> implements ArrayLike<T>, Iterable<T> {
|
|
|
329
406
|
const matcher: any = match;
|
|
330
407
|
if (typeof match !== 'string')
|
|
331
408
|
return nodes.filter((node: any, index: number) => typeof matcher === 'function' ? matcher.call(node, index, node) : matcher?.cheerio ? matcher.toArray().includes(node) : matcher === node);
|
|
332
|
-
const document = this?.
|
|
409
|
+
const document = this?.[DOCUMENT] ?? nodeBinding(root)?.document ?? nodeBinding(nodes[0])?.document ?? routeDocument(new (requireNative().NativeDocument)('', false));
|
|
333
410
|
const ids = importNodes(document, nodes);
|
|
334
411
|
const selected = new Set(operation<number[]>(document, 'matches', { ids, selector: match, options: { xmlMode } }));
|
|
335
412
|
return nodes.filter((node: any) => { const id = nodeId(node, document); return id !== undefined && selected.has(id); });
|
|
@@ -359,40 +436,63 @@ export class Cheerio<T = AnyNode> implements ArrayLike<T>, Iterable<T> {
|
|
|
359
436
|
return this;
|
|
360
437
|
if (index < 0)
|
|
361
438
|
index += this.length;
|
|
362
|
-
|
|
439
|
+
const item = this._itemAt(index);
|
|
440
|
+
return this._fromValues(item === undefined ? [] : [item]);
|
|
363
441
|
}
|
|
364
442
|
get<T>(this: Cheerio<T>, i: number): T | undefined;
|
|
365
443
|
get<T>(this: Cheerio<T>): T[];
|
|
366
|
-
get(this: Cheerio<any>, index?: any): any { return index == null ? this.toArray() : this
|
|
444
|
+
get(this: Cheerio<any>, index?: any): any { return index == null ? this.toArray() : this._itemAt(index < 0 ? this.length + index : index); }
|
|
367
445
|
toArray<T>(this: Cheerio<T>): T[];
|
|
368
|
-
toArray(this: Cheerio<any>): any { return
|
|
446
|
+
toArray(this: Cheerio<any>): any { return this._items(); }
|
|
447
|
+
/** @internal Every item, read without a proxy trap per index. */
|
|
448
|
+
_items(): T[] {
|
|
449
|
+
if (this[IS_PLAIN]) {
|
|
450
|
+
const length = this.length;
|
|
451
|
+
const items: T[] = new Array(length);
|
|
452
|
+
for (let i = 0; i < length; i++) items[i] = this[i];
|
|
453
|
+
return items;
|
|
454
|
+
}
|
|
455
|
+
const values = this[VALUES], ids = this[IDS], document = this[DOCUMENT];
|
|
456
|
+
return values ? values.slice() : ids.map((id) => getNode(document, id) as T);
|
|
457
|
+
}
|
|
458
|
+
/** @internal */ _itemAt(index: number): T | undefined {
|
|
459
|
+
if (this[IS_PLAIN]) return this[index];
|
|
460
|
+
const values = this[VALUES];
|
|
461
|
+
if (values) return values[index];
|
|
462
|
+
const id = this[IDS][index];
|
|
463
|
+
return id === undefined ? undefined : getNode(this[DOCUMENT], id) as T;
|
|
464
|
+
}
|
|
369
465
|
slice<T>(this: Cheerio<T>, start?: number, end?: number): Cheerio<T>;
|
|
370
|
-
slice(this: Cheerio<any>, start?: any, end?: any): any {
|
|
466
|
+
slice(this: Cheerio<any>, start?: any, end?: any): any { const ids = this.ids; return this[VALUES] ? this._fromValues(this[VALUES].slice(start, end)) : this._fromIds(ids.slice(start, end)); }
|
|
371
467
|
end<T>(this: Cheerio<T>): Cheerio<AnyNode>;
|
|
372
468
|
end(this: Cheerio<any>): any { return this.prevObject ?? this._make([]); }
|
|
373
469
|
add<S extends AnyNode, T extends AnyNode>(this: Cheerio<T>, other: string | Cheerio<S> | S | S[], context?: Cheerio<S> | string): Cheerio<S | T>;
|
|
374
|
-
add<R extends AnyNode>(this: Cheerio<any>, other: any, context?: any): any {
|
|
470
|
+
add<R extends AnyNode>(this: Cheerio<any>, other: any, context?: any): any {
|
|
471
|
+
const added = this[API](other, context);
|
|
472
|
+
// Ids of another copy of the package belong to its own id space.
|
|
473
|
+
const addedIds = added[DOCUMENT] && sameNodeSpace(this[DOCUMENT], added[DOCUMENT]) ? added.ids : importNodes(this[DOCUMENT], added.toArray());
|
|
474
|
+
return this._fromIds(this.op('uniqueSort', { ids: [...this.ids, ...addedIds] }));
|
|
475
|
+
}
|
|
375
476
|
addBack<T extends AnyNode>(this: Cheerio<T>, selector?: string): Cheerio<AnyNode>;
|
|
376
477
|
addBack(this: Cheerio<any>, selector?: any): any { return this.prevObject ? this.add(selector ? this.prevObject.filter(selector) : this.prevObject) as any : this as any; }
|
|
377
478
|
index<T extends AnyNode>(this: Cheerio<T>, selectorOrNeedle?: string | Cheerio<AnyNode> | AnyNode): number;
|
|
378
|
-
index(this: Cheerio<any>, selector?: any): any { const haystack = selector == null ? this.parent().children() : typeof selector === 'string' ? this
|
|
479
|
+
index(this: Cheerio<any>, selector?: any): any { const haystack = selector == null ? this.parent().children() : typeof selector === 'string' ? this[API](selector) : this; const needle = selector == null || typeof selector === 'string' ? this[0] : selector?.cheerio ? selector[0] : selector; return haystack.toArray().indexOf(needle); }
|
|
379
480
|
splice(start: number, deleteCount?: number): any[];
|
|
380
481
|
splice(start: number, deleteCount: number, ...items: any[]): any[];
|
|
381
482
|
splice(...args: any[]): any[] { return Reflect.apply(Array.prototype.splice, this, args); }
|
|
382
483
|
[Symbol.iterator](): Iterator<T>;
|
|
383
|
-
|
|
384
|
-
yield this[i]; }
|
|
484
|
+
[Symbol.iterator](): IterableIterator<T> { return this._items()[Symbol.iterator](); }
|
|
385
485
|
/** @internal */ _insert(position: string, content: any[]): this {
|
|
386
|
-
const targets = this.ids.filter((id) => ['root', 'tag', 'script', 'style', 'cdata'].includes(nodeRecord(this
|
|
486
|
+
const targets = this.ids.filter((id) => ['root', 'tag', 'script', 'style', 'cdata'].includes(nodeRecord(this[DOCUMENT], id).type));
|
|
387
487
|
if (!targets.length)
|
|
388
488
|
return this;
|
|
389
489
|
if (typeof content[0] === 'function')
|
|
390
490
|
return (this as Cheerio<any>).each((index, node: any) => {
|
|
391
491
|
if (!targets.includes(this.ids[index]))
|
|
392
492
|
return;
|
|
393
|
-
if ((position === 'before' || position === 'after') && nodeRecord(this
|
|
493
|
+
if ((position === 'before' || position === 'after') && nodeRecord(this[DOCUMENT], this.ids[index]).parent == null)
|
|
394
494
|
return;
|
|
395
|
-
const value = content[0].call(node, index, this.
|
|
495
|
+
const value = content[0].call(node, index, this[DOCUMENT].innerHtml(this.ids[index]));
|
|
396
496
|
let nodes = this._domIds(value);
|
|
397
497
|
if (index < this.length - 1)
|
|
398
498
|
nodes = this.op('clone', { ids: nodes, deep: true });
|
|
@@ -421,15 +521,15 @@ export class Cheerio<T = AnyNode> implements ArrayLike<T>, Iterable<T> {
|
|
|
421
521
|
] | BasicAcceptedElems<AnyNode>[]): Cheerio<T>;
|
|
422
522
|
after(this: Cheerio<any>, ...content: any[]): any { return this._insert('after', content); }
|
|
423
523
|
appendTo<T extends AnyNode>(this: Cheerio<T>, target: BasicAcceptedElems<AnyNode>): Cheerio<T>;
|
|
424
|
-
appendTo(this: Cheerio<any>, target: any): any { this
|
|
524
|
+
appendTo(this: Cheerio<any>, target: any): any { this[API](target).append(this); return this; }
|
|
425
525
|
prependTo<T extends AnyNode>(this: Cheerio<T>, target: BasicAcceptedElems<AnyNode>): Cheerio<T>;
|
|
426
|
-
prependTo(this: Cheerio<any>, target: any): any { this
|
|
526
|
+
prependTo(this: Cheerio<any>, target: any): any { this[API](target).prepend(this); return this; }
|
|
427
527
|
/** @internal */ _insertRelative(target: any, position: string): Cheerio<T> {
|
|
428
|
-
const targets = this
|
|
528
|
+
const targets = this[API](target).ids;
|
|
429
529
|
(this as Cheerio<any>).remove();
|
|
430
530
|
const inserted: number[] = [];
|
|
431
531
|
for (const id of targets) {
|
|
432
|
-
if (nodeRecord(this
|
|
532
|
+
if (nodeRecord(this[DOCUMENT], id).parent == null)
|
|
433
533
|
continue;
|
|
434
534
|
const clones = this.op<number[]>('clone', { ids: this.ids, deep: true });
|
|
435
535
|
this.op('insert', { targetIds: [id], nodeIds: clones, position });
|
|
@@ -446,7 +546,7 @@ export class Cheerio<T = AnyNode> implements ArrayLike<T>, Iterable<T> {
|
|
|
446
546
|
replaceWith<T extends AnyNode>(this: Cheerio<T>, content: AcceptedElems<AnyNode>): Cheerio<T>;
|
|
447
547
|
replaceWith(this: Cheerio<any>, content: any): any {
|
|
448
548
|
return this.each((index, node: any) => {
|
|
449
|
-
if (nodeRecord(this
|
|
549
|
+
if (nodeRecord(this[DOCUMENT], this.ids[index]).parent == null)
|
|
450
550
|
return;
|
|
451
551
|
const value = typeof content === 'function' ? content.call(node, index, node) : content;
|
|
452
552
|
this.op('insert', { targetIds: [this.ids[index]], nodeIds: this._domIds(value), position: 'replace' });
|
|
@@ -462,13 +562,13 @@ export class Cheerio<T = AnyNode> implements ArrayLike<T>, Iterable<T> {
|
|
|
462
562
|
html<T extends AnyNode>(this: Cheerio<T>, str: string | Cheerio<T>): Cheerio<T>;
|
|
463
563
|
html(this: Cheerio<any>, content?: any): any {
|
|
464
564
|
if (content === undefined)
|
|
465
|
-
return this.length && ['root', 'tag', 'script', 'style', 'cdata'].includes(nodeRecord(this
|
|
565
|
+
return this.length && ['root', 'tag', 'script', 'style', 'cdata'].includes(nodeRecord(this[DOCUMENT], this.ids[0]).type) ? this[DOCUMENT].innerHtml(this.ids[0]) : null;
|
|
466
566
|
if (!content?.cheerio) {
|
|
467
567
|
this.op('html', { ids: this.ids, value: String(content) });
|
|
468
568
|
return this;
|
|
469
569
|
}
|
|
470
570
|
for (const id of this.ids) {
|
|
471
|
-
const nodes = content?.cheerio ? importNodes(this
|
|
571
|
+
const nodes = content?.cheerio ? importNodes(this[DOCUMENT], content.toArray()) : this._domIds(String(content), id);
|
|
472
572
|
this.op('setNode', { id, field: 'children', value: nodes });
|
|
473
573
|
}
|
|
474
574
|
return this;
|
|
@@ -477,26 +577,26 @@ export class Cheerio<T = AnyNode> implements ArrayLike<T>, Iterable<T> {
|
|
|
477
577
|
text<T extends AnyNode>(this: Cheerio<T>, str: string | ((this: AnyNode, i: number, text: string) => string)): Cheerio<T>;
|
|
478
578
|
text(this: Cheerio<any>, content?: any): any {
|
|
479
579
|
if (content === undefined)
|
|
480
|
-
return this.
|
|
580
|
+
return this[DOCUMENT].text(this.ids);
|
|
481
581
|
if (typeof content !== 'function') {
|
|
482
582
|
this.op('text', { ids: this.ids, value: String(content) });
|
|
483
583
|
return this;
|
|
484
584
|
}
|
|
485
585
|
for (let i = 0; i < this.ids.length; i++) {
|
|
486
586
|
const id = this.ids[i];
|
|
487
|
-
const value = typeof content === 'function' ? content.call(getNode(this
|
|
587
|
+
const value = typeof content === 'function' ? content.call(getNode(this[DOCUMENT], id), i, this[DOCUMENT].text([id])) : content;
|
|
488
588
|
this.op('text', { ids: [id], value: String(value) });
|
|
489
589
|
}
|
|
490
590
|
return this;
|
|
491
591
|
}
|
|
492
592
|
toString<T extends AnyNode>(this: Cheerio<T>): string;
|
|
493
|
-
toString(this: Cheerio<any>): any { return this.
|
|
593
|
+
toString(this: Cheerio<any>): any { return this[DOCUMENT].outerHtml(this.ids); }
|
|
494
594
|
clone<T extends AnyNode>(this: Cheerio<T>): Cheerio<T>;
|
|
495
595
|
clone(this: Cheerio<any>): any { return this._fromIds(this.op('clone', { ids: this.ids, deep: true })); }
|
|
496
596
|
/** @internal */ _deepest(id: number, tagsOnly = false): number {
|
|
497
597
|
for (;;) {
|
|
498
598
|
const children = this.op<number[]>('traverse', { ids: [id], kind: 'children' });
|
|
499
|
-
const child = tagsOnly ? children.find((candidate) => nodeRecord(this
|
|
599
|
+
const child = tagsOnly ? children.find((candidate) => nodeRecord(this[DOCUMENT], candidate).type === 'tag') : children[0];
|
|
500
600
|
if (child === undefined)
|
|
501
601
|
return id;
|
|
502
602
|
id = child;
|
|
@@ -506,25 +606,25 @@ export class Cheerio<T = AnyNode> implements ArrayLike<T>, Iterable<T> {
|
|
|
506
606
|
const lastParent = (this as Cheerio<any>).parents().last();
|
|
507
607
|
for (let i = 0; i < this.length; i++) {
|
|
508
608
|
const id = this.ids[i];
|
|
509
|
-
const node = getNode(this
|
|
609
|
+
const node = getNode(this[DOCUMENT], id);
|
|
510
610
|
const value = typeof content === 'function' ? content.call(node, i, node) : typeof content === 'string' && !isMarkup(content) ? lastParent.find(content).clone() : content;
|
|
511
611
|
let wrappers = this._domIds(value);
|
|
512
612
|
if (i < this.length - 1)
|
|
513
613
|
wrappers = this.op('clone', { ids: wrappers, deep: true });
|
|
514
614
|
const wrapper = wrappers[0];
|
|
515
|
-
if (wrapper === undefined || !['root', 'tag', 'script', 'style'].includes(nodeRecord(this
|
|
615
|
+
if (wrapper === undefined || !['root', 'tag', 'script', 'style'].includes(nodeRecord(this[DOCUMENT], wrapper).type))
|
|
516
616
|
continue;
|
|
517
|
-
const deepest = this._deepest(wrapper);
|
|
518
617
|
if (inner) {
|
|
519
|
-
if (!['root', 'tag', 'script', 'style', 'cdata'].includes(nodeRecord(this
|
|
618
|
+
if (!['root', 'tag', 'script', 'style', 'cdata'].includes(nodeRecord(this[DOCUMENT], id).type))
|
|
520
619
|
continue;
|
|
521
620
|
const contents = this.op<number[]>('traverse', { ids: [id], kind: 'contents' });
|
|
522
|
-
this.op('setNode', { id:
|
|
621
|
+
this.op('setNode', { id: this._deepest(wrapper), field: 'children', value: contents });
|
|
523
622
|
this.op('setNode', { id, field: 'children', value: [wrapper] });
|
|
524
623
|
}
|
|
525
|
-
else if (nodeRecord(this
|
|
526
|
-
|
|
527
|
-
this.op('
|
|
624
|
+
else if (nodeRecord(this[DOCUMENT], id).parent != null) {
|
|
625
|
+
// A wrapper from another document gets new ids when the insertion moves it here.
|
|
626
|
+
const [inserted = wrapper] = this.op<number[]>('insert', { targetIds: [id], nodeIds: [wrapper], position: 'before' });
|
|
627
|
+
this.op('setNode', { id: this._deepest(inserted), field: 'children', value: [id] });
|
|
528
628
|
}
|
|
529
629
|
}
|
|
530
630
|
return this;
|
|
@@ -541,7 +641,7 @@ export class Cheerio<T = AnyNode> implements ArrayLike<T>, Iterable<T> {
|
|
|
541
641
|
return this;
|
|
542
642
|
const first = this[0];
|
|
543
643
|
const wrappers = this._make(typeof content === 'function' ? content.call(first, 0, first) : content).insertBefore(first);
|
|
544
|
-
const root = [...wrappers.ids].reverse().find((id) => nodeRecord(this
|
|
644
|
+
const root = [...wrappers.ids].reverse().find((id) => nodeRecord(this[DOCUMENT], id).type === 'tag');
|
|
545
645
|
if (root !== undefined)
|
|
546
646
|
this.op('insert', { targetIds: [this._deepest(root, true)], nodeIds: this.ids, position: 'append' });
|
|
547
647
|
return this;
|
|
@@ -553,6 +653,20 @@ export class Cheerio<T = AnyNode> implements ArrayLike<T>, Iterable<T> {
|
|
|
553
653
|
extract<M extends ExtractMap>(this: Cheerio<any>, map: any): any { return extract.call(this as any, map) as ExtractedMap<M>; }
|
|
554
654
|
}
|
|
555
655
|
Object.defineProperty(Cheerio.prototype, 'cheerio', { value: '[cheerio object]', writable: true, configurable: true });
|
|
656
|
+
const inspectCustom = Symbol.for('nodejs.util.inspect.custom');
|
|
657
|
+
// Inspection shows the selection's own string keys, as Cheerio's selections
|
|
658
|
+
// have, without the internals stored under symbols.
|
|
659
|
+
Object.defineProperty(Cheerio.prototype, inspectCustom, {
|
|
660
|
+
configurable: true, writable: true,
|
|
661
|
+
value(this: Cheerio<any>, depth: number, options: { stylize(text: string, style: string): string }, inspect: (value: unknown, options: object) => string): string {
|
|
662
|
+
const prototype = Object.getPrototypeOf(this);
|
|
663
|
+
if (depth < 0) return options.stylize(`[${prototype.constructor.name}]`, 'special');
|
|
664
|
+
const shown: Record<string | symbol, unknown> = Object.create(prototype);
|
|
665
|
+
Object.defineProperty(shown, inspectCustom, { value: undefined });
|
|
666
|
+
for (const key of Object.keys(this)) shown[key] = (this as any)[key];
|
|
667
|
+
return inspect(shown, { ...options, depth });
|
|
668
|
+
},
|
|
669
|
+
});
|
|
556
670
|
export interface Cheerio<T = AnyNode> extends AttributeMethods<T> {
|
|
557
671
|
}
|
|
558
672
|
installAttributes(Cheerio);
|