0
# Resources
1
2
jsdom provides two classes for managing external resources and HTTP cookies: ResourceLoader for fetching resources (scripts, stylesheets, images) and CookieJar for cookie storage. Both can be customized for advanced use cases.
3
4
## Capabilities
5
6
### ResourceLoader Class
7
8
Handles loading of external resources including scripts, stylesheets, images, and iframes. Supports HTTP/HTTPS, file, and data URLs.
9
10
#### Constructor
11
12
```javascript { .api }
13
/**
14
* Create a resource loader for fetching external resources
15
* @param options - Configuration options
16
* @param options.strictSSL - Require valid SSL certificates (default: true)
17
* @param options.proxy - HTTP proxy URL
18
* @param options.userAgent - Custom User-Agent header
19
*/
20
class ResourceLoader {
21
constructor(options?: {
22
strictSSL?: boolean;
23
proxy?: string;
24
userAgent?: string;
25
});
26
}
27
```
28
29
**Options:**
30
31
- **`strictSSL`** (default: `true`): Require valid SSL certificates for HTTPS requests
32
- **`proxy`** (optional): HTTP proxy address (e.g., `"http://127.0.0.1:9001"`)
33
- **`userAgent`** (optional): Custom User-Agent header. Default: `` `Mozilla/5.0 (${process.platform}) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/${version}` ``
34
35
**Usage Examples:**
36
37
```javascript
38
const { JSDOM, ResourceLoader } = require("jsdom");
39
40
// Default resource loader
41
const loader1 = new ResourceLoader();
42
43
// Custom configuration
44
const loader2 = new ResourceLoader({
45
userAgent: "My Custom Bot/1.0",
46
strictSSL: false, // Allow self-signed certificates
47
proxy: "http://127.0.0.1:9001"
48
});
49
50
const dom = new JSDOM(`<script src="https://example.com/script.js"></script>`, {
51
url: "https://example.com/",
52
resources: loader2,
53
runScripts: "dangerously"
54
});
55
```
56
57
#### fetch()
58
59
```javascript { .api }
60
/**
61
* Fetch a resource from a URL
62
* @param urlString - URL to fetch
63
* @param options - Request options
64
* @param options.accept - Accept header value
65
* @param options.cookieJar - Cookie jar for request
66
* @param options.referrer - Referrer URL
67
* @param options.element - Element requesting the resource
68
* @returns Promise for Buffer with additional properties
69
*/
70
fetch(
71
urlString: string,
72
options?: {
73
accept?: string;
74
cookieJar?: CookieJar;
75
referrer?: string;
76
element?: Element;
77
}
78
): Promise<Buffer> & {
79
abort(): void;
80
response?: Response;
81
href?: string;
82
getHeader(name: string): string | undefined;
83
}
84
```
85
86
Fetches a resource from a URL. Returns a Promise for a Buffer with additional methods and properties.
87
88
**Parameters:**
89
90
- **`urlString`**: URL to fetch
91
- **`options.accept`** (optional): Accept header value
92
- **`options.cookieJar`** (optional): CookieJar for cookie handling
93
- **`options.referrer`** (optional): Referrer URL for Referer header
94
- **`options.element`** (optional): DOM element requesting the resource (for logging)
95
96
**Returns:** Promise for Buffer with:
97
98
- **`abort()`**: Method to cancel the request
99
- **`response`**: HTTP response object (set when response received)
100
- **`href`**: Final URL after redirects (set when complete)
101
- **`getHeader(name)`**: Get request header value
102
103
**Supported URL Schemes:**
104
105
- **`http://` and `https://`**: HTTP requests with automatic redirect following
106
- **`file://`**: Local file reading
107
- **`data://`**: Data URL parsing
108
109
**HTTP Behavior:**
110
111
- Follows redirects automatically
112
- Returns 2xx status codes as success
113
- Rejects for non-2xx status codes
114
- Default headers:
115
- `User-Agent`: From constructor option
116
- `Accept-Language`: `"en"`
117
- `Accept-Encoding`: `"gzip"`
118
- `Accept`: From options or `"*/*"`
119
- `Referer`: From options (if provided)
120
121
**Usage Examples:**
122
123
```javascript
124
const { ResourceLoader } = require("jsdom");
125
126
const loader = new ResourceLoader();
127
128
// Simple fetch
129
loader.fetch("https://example.com/data.json").then(buffer => {
130
const json = JSON.parse(buffer.toString());
131
console.log(json);
132
}).catch(error => {
133
console.error("Fetch failed:", error.message);
134
});
135
136
// With options
137
const { CookieJar } = require("jsdom");
138
const cookieJar = new CookieJar();
139
140
const promise = loader.fetch("https://example.com/api/data", {
141
accept: "application/json",
142
cookieJar: cookieJar,
143
referrer: "https://example.com/page"
144
});
145
146
promise.then(buffer => {
147
console.log("Final URL:", promise.href);
148
console.log("Status:", promise.response.statusCode);
149
console.log("Data:", buffer.toString());
150
});
151
152
// Cancel request
153
const cancelablePromise = loader.fetch("https://example.com/large-file.zip");
154
155
setTimeout(() => {
156
cancelablePromise.abort(); // Cancel after 1 second
157
}, 1000);
158
159
cancelablePromise.catch(error => {
160
if (error.isAbortError) {
161
console.log("Request cancelled");
162
}
163
});
164
165
// File URL
166
loader.fetch("file:///path/to/local/file.html").then(buffer => {
167
console.log(buffer.toString());
168
});
169
170
// Data URL
171
loader.fetch("data:text/plain,Hello%20World").then(buffer => {
172
console.log(buffer.toString()); // "Hello World"
173
});
174
```
175
176
#### Subclassing ResourceLoader
177
178
Create custom resource loaders by extending the ResourceLoader class and overriding the `fetch()` method.
179
180
**Usage Examples:**
181
182
```javascript
183
const { JSDOM, ResourceLoader } = require("jsdom");
184
185
// Custom resource loader with overrides
186
class CustomResourceLoader extends ResourceLoader {
187
fetch(url, options) {
188
// Intercept specific URLs
189
if (url === "https://example.com/special-script.js") {
190
return Promise.resolve(Buffer.from("window.special = true;"));
191
}
192
193
// Mock API endpoints
194
if (url.startsWith("https://api.example.com/")) {
195
const mockData = { status: "ok", data: [] };
196
return Promise.resolve(Buffer.from(JSON.stringify(mockData)));
197
}
198
199
// Block certain domains
200
if (url.includes("blocked-domain.com")) {
201
return Promise.reject(new Error("Domain blocked"));
202
}
203
204
// Log all requests
205
console.log("Fetching:", url);
206
if (options.element) {
207
console.log(" Requested by:", options.element.localName);
208
}
209
210
// Delegate to parent for other URLs
211
return super.fetch(url, options);
212
}
213
}
214
215
const customLoader = new CustomResourceLoader();
216
217
const dom = new JSDOM(`
218
<script src="https://example.com/special-script.js"></script>
219
<script src="https://api.example.com/data"></script>
220
<img src="https://example.com/image.png">
221
`, {
222
url: "https://example.com/",
223
resources: customLoader,
224
runScripts: "dangerously"
225
});
226
```
227
228
### CookieJar Class
229
230
Manages HTTP cookies with browser-like behavior. Extends `tough-cookie.CookieJar` with loose mode enabled by default.
231
232
#### Constructor
233
234
```javascript { .api }
235
/**
236
* Create a cookie jar for HTTP cookie storage
237
* Extends tough-cookie.CookieJar with looseMode: true by default
238
* @param store - Cookie store backend (optional)
239
* @param options - Options passed to tough-cookie
240
*/
241
class CookieJar extends ToughCookieJar {
242
constructor(store?: CookieStore, options?: object);
243
}
244
```
245
246
**Parameters:**
247
248
- **`store`** (optional): Cookie store backend from tough-cookie
249
- **`options`** (optional): Options passed to tough-cookie (jsdom sets `looseMode: true` by default for better browser compatibility)
250
251
**Usage Examples:**
252
253
```javascript
254
const { JSDOM, CookieJar } = require("jsdom");
255
256
// Default cookie jar
257
const jar1 = new CookieJar();
258
259
// With custom store
260
const { MemoryCookieStore } = require("tough-cookie");
261
const jar2 = new CookieJar(new MemoryCookieStore());
262
263
// Use with jsdom
264
const jar3 = new CookieJar();
265
const dom = new JSDOM(``, {
266
url: "https://example.com/",
267
cookieJar: jar3
268
});
269
270
// Set cookie via document.cookie
271
dom.window.document.cookie = "session=abc123; path=/";
272
273
// Access cookies
274
const cookies = jar3.getCookiesSync("https://example.com/");
275
console.log(cookies);
276
```
277
278
#### Cookie Methods
279
280
CookieJar inherits all methods from tough-cookie. Most commonly used:
281
282
```javascript { .api }
283
/**
284
* Set a cookie synchronously
285
* @param cookieOrString - Cookie object or cookie string
286
* @param currentUrl - URL for cookie context
287
* @param options - Cookie options
288
* @returns Cookie object
289
*/
290
setCookieSync(
291
cookieOrString: string | Cookie,
292
currentUrl: string,
293
options?: object
294
): Cookie
295
296
/**
297
* Get cookies synchronously
298
* @param currentUrl - URL for cookie context
299
* @param options - Cookie options
300
* @returns Array of Cookie objects
301
*/
302
getCookiesSync(
303
currentUrl: string,
304
options?: object
305
): Cookie[]
306
307
/**
308
* Set a cookie asynchronously
309
* @param cookieOrString - Cookie object or cookie string
310
* @param currentUrl - URL for cookie context
311
* @param options - Cookie options
312
* @param callback - Callback function
313
*/
314
setCookie(
315
cookieOrString: string | Cookie,
316
currentUrl: string,
317
options?: object,
318
callback?: (error: Error | null, cookie: Cookie) => void
319
): void
320
321
/**
322
* Get cookies asynchronously
323
* @param currentUrl - URL for cookie context
324
* @param options - Cookie options
325
* @param callback - Callback function
326
*/
327
getCookies(
328
currentUrl: string,
329
options?: object,
330
callback?: (error: Error | null, cookies: Cookie[]) => void
331
): void
332
333
/**
334
* Get cookies as string for Cookie header
335
* @param currentUrl - URL for cookie context
336
* @param options - Cookie options
337
* @param callback - Callback function
338
*/
339
getCookieString(
340
currentUrl: string,
341
options?: object,
342
callback?: (error: Error | null, cookieString: string) => void
343
): void
344
345
/**
346
* Get cookies as string synchronously
347
* @param currentUrl - URL for cookie context
348
* @param options - Cookie options
349
* @returns Cookie string for Cookie header
350
*/
351
getCookieStringSync(
352
currentUrl: string,
353
options?: object
354
): string
355
```
356
357
**Usage Examples:**
358
359
```javascript
360
const { CookieJar } = require("jsdom");
361
362
const jar = new CookieJar();
363
364
// Set cookies
365
jar.setCookieSync("session=abc123; Path=/; HttpOnly", "https://example.com/");
366
jar.setCookieSync("user=john; Path=/; Max-Age=3600", "https://example.com/");
367
368
// Get cookies
369
const cookies = jar.getCookiesSync("https://example.com/");
370
console.log(cookies); // Array of Cookie objects
371
372
// Get cookie string (for HTTP Cookie header)
373
const cookieString = jar.getCookieStringSync("https://example.com/");
374
console.log(cookieString); // "session=abc123; user=john"
375
376
// Async operations
377
jar.setCookie("token=xyz789", "https://example.com/", (error, cookie) => {
378
if (error) {
379
console.error("Failed to set cookie:", error);
380
} else {
381
console.log("Cookie set:", cookie.toString());
382
}
383
});
384
385
jar.getCookies("https://example.com/", (error, cookies) => {
386
if (!error) {
387
console.log("Cookies:", cookies);
388
}
389
});
390
```
391
392
#### Cookie Behavior
393
394
**Accessibility:**
395
396
- Cookies on same domain as document URL accessible via `document.cookie`
397
- HTTP-only cookies **not** accessible via `document.cookie`
398
- All cookies in jar affect subresource fetching (including HTTP-only)
399
400
**Sharing:**
401
402
- Cookie jars can be shared across multiple jsdom instances
403
- Useful for maintaining session across page navigations
404
405
**Integration:**
406
407
- Used automatically by `JSDOM.fromURL()` for HTTP requests
408
- Cookies from `Set-Cookie` headers stored in jar
409
- Cookies sent in `Cookie` header for requests
410
411
**Usage Examples:**
412
413
```javascript
414
const { JSDOM, CookieJar } = require("jsdom");
415
416
// Shared cookie jar across instances
417
const sharedJar = new CookieJar();
418
419
const dom1 = new JSDOM(``, {
420
url: "https://example.com/page1",
421
cookieJar: sharedJar
422
});
423
424
// Set cookie in first instance
425
dom1.window.document.cookie = "shared=value";
426
427
const dom2 = new JSDOM(``, {
428
url: "https://example.com/page2",
429
cookieJar: sharedJar
430
});
431
432
// Cookie available in second instance
433
console.log(dom2.window.document.cookie); // "shared=value"
434
435
// HTTP-only cookies (not accessible via document.cookie)
436
sharedJar.setCookieSync(
437
"session=secret; Path=/; HttpOnly",
438
"https://example.com/"
439
);
440
441
console.log(dom1.window.document.cookie); // "shared=value" (no session cookie)
442
443
// But HTTP-only cookie sent in HTTP requests
444
JSDOM.fromURL("https://example.com/api", {
445
cookieJar: sharedJar
446
}).then(dom => {
447
// Request included both shared and session cookies
448
});
449
```
450
451
### toughCookie Module Export
452
453
jsdom re-exports the `tough-cookie` module for direct access to cookie utilities.
454
455
```javascript { .api }
456
/**
457
* Re-export of tough-cookie module for cookie utilities
458
*/
459
const toughCookie: ToughCookieModule
460
```
461
462
**Provides access to:**
463
464
- `Cookie` class
465
- `CookieJar` class
466
- `Store` classes
467
- Parsing and serialization utilities
468
469
**Usage Examples:**
470
471
```javascript
472
const { toughCookie } = require("jsdom");
473
474
// Use Cookie class
475
const cookie = new toughCookie.Cookie({
476
key: "session",
477
value: "abc123",
478
domain: "example.com",
479
path: "/",
480
httpOnly: true,
481
maxAge: 3600
482
});
483
484
console.log(cookie.toString());
485
// "session=abc123; Domain=example.com; Path=/; HttpOnly; Max-Age=3600"
486
487
// Parse cookie string
488
const parsed = toughCookie.Cookie.parse("user=john; Path=/; Secure");
489
console.log(parsed.key); // "user"
490
console.log(parsed.value); // "john"
491
console.log(parsed.secure); // true
492
```
493
494
## Common Usage Patterns
495
496
### Custom Resource Caching
497
498
```javascript
499
const { JSDOM, ResourceLoader } = require("jsdom");
500
501
class CachingResourceLoader extends ResourceLoader {
502
constructor(options) {
503
super(options);
504
this.cache = new Map();
505
}
506
507
fetch(url, options) {
508
// Check cache
509
if (this.cache.has(url)) {
510
console.log("Cache hit:", url);
511
return Promise.resolve(this.cache.get(url));
512
}
513
514
// Fetch and cache
515
return super.fetch(url, options).then(buffer => {
516
this.cache.set(url, buffer);
517
console.log("Cached:", url);
518
return buffer;
519
});
520
}
521
}
522
523
const loader = new CachingResourceLoader();
524
const dom = new JSDOM(`
525
<script src="https://example.com/lib.js"></script>
526
`, {
527
url: "https://example.com/",
528
resources: loader,
529
runScripts: "dangerously"
530
});
531
```
532
533
### Session Management
534
535
```javascript
536
const { JSDOM, CookieJar } = require("jsdom");
537
538
async function navigateWithSession(urls) {
539
const sessionJar = new CookieJar();
540
541
for (const url of urls) {
542
const dom = await JSDOM.fromURL(url, {
543
cookieJar: sessionJar,
544
resources: "usable",
545
runScripts: "dangerously"
546
});
547
548
console.log("Page:", dom.window.document.title);
549
console.log("Cookies:", sessionJar.getCookieStringSync(url));
550
551
// Session maintained across requests
552
}
553
}
554
555
navigateWithSession([
556
"https://example.com/login",
557
"https://example.com/dashboard",
558
"https://example.com/profile"
559
]);
560
```
561
562
### Mock API Responses
563
564
```javascript
565
const { JSDOM, ResourceLoader } = require("jsdom");
566
567
class MockAPILoader extends ResourceLoader {
568
constructor(mocks, options) {
569
super(options);
570
this.mocks = mocks;
571
}
572
573
fetch(url, options) {
574
// Check if URL should be mocked
575
for (const [pattern, response] of Object.entries(this.mocks)) {
576
if (url.includes(pattern)) {
577
console.log("Mocked:", url);
578
return Promise.resolve(Buffer.from(JSON.stringify(response)));
579
}
580
}
581
582
// Real fetch for non-mocked URLs
583
return super.fetch(url, options);
584
}
585
}
586
587
const loader = new MockAPILoader({
588
"/api/users": { users: [{ id: 1, name: "Alice" }] },
589
"/api/posts": { posts: [{ id: 1, title: "Test Post" }] }
590
});
591
592
const dom = new JSDOM(`
593
<script>
594
fetch("/api/users")
595
.then(r => r.json())
596
.then(data => console.log(data));
597
</script>
598
`, {
599
url: "https://example.com/",
600
resources: loader,
601
runScripts: "dangerously"
602
});
603
```
604
605
## Types
606
607
```javascript { .api }
608
interface ResourceLoaderOptions {
609
strictSSL?: boolean;
610
proxy?: string;
611
userAgent?: string;
612
}
613
614
interface FetchOptions {
615
accept?: string;
616
cookieJar?: CookieJar;
617
referrer?: string;
618
element?: Element;
619
}
620
621
interface FetchPromise extends Promise<Buffer> {
622
abort(): void;
623
response?: Response;
624
href?: string;
625
getHeader(name: string): string | undefined;
626
}
627
628
interface Cookie {
629
key: string;
630
value: string;
631
domain?: string;
632
path?: string;
633
secure?: boolean;
634
httpOnly?: boolean;
635
expires?: Date;
636
maxAge?: number;
637
sameSite?: "strict" | "lax" | "none";
638
}
639
```
640