6
6
} from './options' ;
7
7
import * as staticMethods from './static' ;
8
8
import { Cheerio } from './cheerio' ;
9
+ import { isHtml , isCheerio } from './utils' ;
9
10
import parse from './parse' ;
10
11
import type { Node , Document , Element } from 'domhandler' ;
11
12
import type * as Load from './load' ;
@@ -94,30 +95,113 @@ export function load(
94
95
}
95
96
96
97
const internalOpts = { ...defaultOptions , ...flattenOptions ( options ) } ;
97
- const root = parse ( content , internalOpts , isDocument ) ;
98
+ const initialRoot = parse ( content , internalOpts , isDocument ) ;
98
99
99
100
/** Create an extended class here, so that extensions only live on one instance. */
100
- class LoadedCheerio < T > extends Cheerio < T > { }
101
-
102
- function initialize < T > (
103
- selector ?: T extends Node
104
- ? string | Cheerio < T > | T [ ] | T
105
- : Cheerio < T > | T [ ] ,
106
- context ?: string | Cheerio < Node > | Node [ ] | Node ,
107
- r : string | Cheerio < Document > | Document | null = root ,
101
+ class LoadedCheerio < T > extends Cheerio < T > {
102
+ _make < T > (
103
+ selector ?: ArrayLike < T > | T | string ,
104
+ context ?: BasicAcceptedElems < Node > | null
105
+ ) : Cheerio < T > {
106
+ const cheerio = initialize ( selector , context ) ;
107
+ cheerio . prevObject = this ;
108
+
109
+ return cheerio ;
110
+ }
111
+ }
112
+
113
+ function initialize < T = Node , S extends string = string > (
114
+ selector ?: ArrayLike < T > | T | S ,
115
+ context ?: BasicAcceptedElems < Node > | null ,
116
+ root : BasicAcceptedElems < Document > = initialRoot ,
108
117
opts ?: CheerioOptions
109
- ) {
110
- return new LoadedCheerio < T > ( selector , context , r , {
118
+ ) : Cheerio < S extends SelectorType ? Element : T > {
119
+ type Result = S extends SelectorType ? Element : T ;
120
+
121
+ // $($)
122
+ if ( selector && isCheerio < Result > ( selector ) ) return selector ;
123
+
124
+ const options = {
111
125
...internalOpts ,
112
126
...flattenOptions ( opts ) ,
113
- } ) ;
127
+ } ;
128
+ const r =
129
+ typeof root === 'string'
130
+ ? [ parse ( root , options , false ) ]
131
+ : 'length' in root
132
+ ? root
133
+ : [ root ] ;
134
+ const rootInstance = isCheerio < Document > ( r )
135
+ ? r
136
+ : new LoadedCheerio < Document > ( r , null , options ) ;
137
+ // Add a cyclic reference, so that calling methods on `_root` never fails.
138
+ rootInstance . _root = rootInstance ;
139
+
140
+ // $(), $(null), $(undefined), $(false)
141
+ if ( ! selector ) {
142
+ return new LoadedCheerio < Result > ( undefined , rootInstance , options ) ;
143
+ }
144
+
145
+ const elements : Node [ ] | undefined =
146
+ typeof selector === 'string' && isHtml ( selector )
147
+ ? // $(<html>)
148
+ parse ( selector , options , false ) . children
149
+ : isNode ( selector )
150
+ ? // $(dom)
151
+ [ selector ]
152
+ : Array . isArray ( selector )
153
+ ? // $([dom])
154
+ selector
155
+ : undefined ;
156
+
157
+ const instance = new LoadedCheerio ( elements , rootInstance , options ) ;
158
+
159
+ if ( elements || ! selector ) {
160
+ return instance as any ;
161
+ }
162
+
163
+ if ( typeof selector !== 'string' ) throw new Error ( '' ) ;
164
+
165
+ // We know that our selector is a string now.
166
+ let search = selector ;
167
+
168
+ const searchContext : Cheerio < Node > | undefined = ! context
169
+ ? // If we don't have a context, maybe we have a root, from loading
170
+ rootInstance
171
+ : typeof context === 'string'
172
+ ? isHtml ( context )
173
+ ? // $('li', '<ul>...</ul>')
174
+ new LoadedCheerio < Document > (
175
+ [ parse ( context , options , false ) ] ,
176
+ rootInstance ,
177
+ options
178
+ )
179
+ : // $('li', 'ul')
180
+ ( ( search = `${ context } ${ search } ` as S ) , rootInstance )
181
+ : isCheerio < Node > ( context )
182
+ ? // $('li', $)
183
+ context
184
+ : // $('li', node), $('li', [nodes])
185
+ new LoadedCheerio < Node > (
186
+ Array . isArray ( context ) ? context : [ context ] ,
187
+ rootInstance ,
188
+ options
189
+ ) ;
190
+
191
+ // If we still don't have a context, return
192
+ if ( ! searchContext ) return instance as any ;
193
+
194
+ /*
195
+ * #id, .class, tag
196
+ */
197
+ return searchContext . find ( search ) as Cheerio < Result > ;
114
198
}
115
199
116
200
// Add in static methods & properties
117
201
Object . assign ( initialize , staticMethods , {
118
202
load,
119
203
// `_root` and `_options` are used in static methods.
120
- _root : root ,
204
+ _root : initialRoot ,
121
205
_options : internalOpts ,
122
206
// Add `fn` for plugins
123
207
fn : LoadedCheerio . prototype ,
@@ -127,3 +211,12 @@ export function load(
127
211
128
212
return initialize as CheerioAPI ;
129
213
}
214
+
215
+ function isNode ( obj : any ) : obj is Node {
216
+ return (
217
+ ! ! obj . name ||
218
+ obj . type === 'root' ||
219
+ obj . type === 'text' ||
220
+ obj . type === 'comment'
221
+ ) ;
222
+ }
0 commit comments