Skip to content

Commit 2b8ed1f

Browse files
authored
Make the context option mergeable (#1459)
1 parent 71c319d commit 2b8ed1f

File tree

5 files changed

+32
-21
lines changed

5 files changed

+32
-21
lines changed

Diff for: readme.md

+1-3
Original file line numberDiff line numberDiff line change
@@ -251,9 +251,7 @@ JSON body. If the `Content-Type` header is not set, it will be set to `applicati
251251

252252
Type: `object`
253253

254-
User data. In contrast to other options, `context` is not enumerable.
255-
256-
**Note:** The object is never merged, it's just passed through. Got will not modify the object in any way.
254+
User data. `context` is shallow merged and enumerable. If it contains non-enumerable properties they will NOT be merged.
257255

258256
It's very useful for storing auth tokens:
259257

Diff for: source/core/index.ts

+3-9
Original file line numberDiff line numberDiff line change
@@ -515,10 +515,7 @@ interface PlainOptions extends URLOptions {
515515
dnsCache?: CacheableLookup | boolean;
516516

517517
/**
518-
User data. In contrast to other options, `context` is not enumerable.
519-
520-
__Note__: The object is never merged, it's just passed through.
521-
Got will not modify the object in any way.
518+
User data. `context` is shallow merged and enumerable. If it contains non-enumerable properties they will NOT be merged.
522519
523520
@example
524521
```
@@ -1139,9 +1136,8 @@ const waitForOpenFile = async (file: ReadStream): Promise<void> => new Promise((
11391136

11401137
const redirectCodes: ReadonlySet<number> = new Set([300, 301, 302, 303, 304, 307, 308]);
11411138

1142-
type NonEnumerableProperty = 'context' | 'body' | 'json' | 'form';
1139+
type NonEnumerableProperty = 'body' | 'json' | 'form';
11431140
const nonEnumerableProperties: NonEnumerableProperty[] = [
1144-
'context',
11451141
'body',
11461142
'json',
11471143
'form'
@@ -1780,9 +1776,7 @@ export default class Request extends Duplex implements RequestEvents<Request> {
17801776
}
17811777

17821778
// `options.context`
1783-
if (!options.context) {
1784-
options.context = {};
1785-
}
1779+
options.context = {...defaults?.context, ...options.context};
17861780

17871781
// `options.hooks`
17881782
const areHooksDefault = options.hooks === defaults?.hooks;

Diff for: source/types.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ export interface Got extends Record<HTTPAlias, GotRequestFunction>, GotRequestFu
371371
- If the parent property is a plain `object` too, both values are merged recursively into a new `object`.
372372
- Otherwise, only the new value is deeply cloned.
373373
- If the new property is an `Array`, it overwrites the old one with a deep clone of the new property.
374-
- Properties that are not enumerable, such as `context`, `body`, `json`, and `form`, will not be merged.
374+
- Properties that are not enumerable, such as `body`, `json`, and `form`, will not be merged.
375375
- Otherwise, the new value is assigned to the key.
376376
377377
**Note:** Only Got options are merged! Custom user options should be defined via [`options.context`](#context).

Diff for: test/arguments.ts

+26-7
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ test('throws if the `searchParams` value is invalid', async t => {
328328
});
329329
});
330330

331-
test('`context` option is not enumerable', withServer, async (t, server, got) => {
331+
test('`context` option is enumerable', withServer, async (t, server, got) => {
332332
server.get('/', echoUrl);
333333

334334
const context = {
@@ -340,8 +340,8 @@ test('`context` option is not enumerable', withServer, async (t, server, got) =>
340340
hooks: {
341341
beforeRequest: [
342342
options => {
343-
t.is(options.context, context);
344-
t.false({}.propertyIsEnumerable.call(options, 'context'));
343+
t.deepEqual(options.context, context);
344+
t.true({}.propertyIsEnumerable.call(options, 'context'));
345345
}
346346
]
347347
}
@@ -360,8 +360,8 @@ test('`context` option is accessible when using hooks', withServer, async (t, se
360360
hooks: {
361361
beforeRequest: [
362362
options => {
363-
t.is(options.context, context);
364-
t.false({}.propertyIsEnumerable.call(options, 'context'));
363+
t.deepEqual(options.context, context);
364+
t.true({}.propertyIsEnumerable.call(options, 'context'));
365365
}
366366
]
367367
}
@@ -375,8 +375,27 @@ test('`context` option is accessible when extending instances', t => {
375375

376376
const instance = got.extend({context});
377377

378-
t.is(instance.defaults.options.context, context);
379-
t.false({}.propertyIsEnumerable.call(instance.defaults.options, 'context'));
378+
t.deepEqual(instance.defaults.options.context, context);
379+
t.true({}.propertyIsEnumerable.call(instance.defaults.options, 'context'));
380+
});
381+
382+
test('`context` option is shallow merged', t => {
383+
const context = {
384+
foo: 'bar'
385+
};
386+
387+
const context2 = {
388+
bar: 'baz'
389+
};
390+
391+
const instance1 = got.extend({context});
392+
393+
t.deepEqual(instance1.defaults.options.context, context);
394+
t.true({}.propertyIsEnumerable.call(instance1.defaults.options, 'context'));
395+
396+
const instance2 = instance1.extend({context: context2});
397+
398+
t.deepEqual(instance2.defaults.options.context, {...context, ...context2});
380399
});
381400

382401
test('throws if `options.encoding` is `null`', async t => {

Diff for: test/hooks.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,7 @@ test('beforeRetry is called with options', withServer, async (t, server, got) =>
426426
beforeRetry: [
427427
(options, error, retryCount) => {
428428
t.is(options.url.hostname, 'localhost');
429-
t.is(options.context, context);
429+
t.deepEqual(options.context, context);
430430
t.truthy(error);
431431
t.true(retryCount! >= 1);
432432
}

0 commit comments

Comments
 (0)