diff --git a/advanced-creation.md b/advanced-creation.md
index fb09aedd3..ef75966a2 100644
--- a/advanced-creation.md
+++ b/advanced-creation.md
@@ -220,7 +220,7 @@ const noUserAgent = got.extend({
```js
const httpbin = got.extend({
- baseUrl: 'https://httpbin.org/'
+ prefixUrl: 'https://httpbin.org/'
});
```
@@ -261,10 +261,10 @@ const merged = got.mergeInstances(controlRedirects, limitDownloadUpload, httpbin
*/
const MEGABYTE = 1048576;
- await merged('http://ipv4.download.thinkbroadband.com/5MB.zip', {downloadLimit: MEGABYTE});
+ await merged('http://ipv4.download.thinkbroadband.com/5MB.zip', {downloadLimit: MEGABYTE, prefixUrl: ''});
// CancelError: Exceeded the download limit of 1048576 bytes
- await merged('https://jigsaw.w3.org/HTTP/300/301.html', {allowedHosts: ['google.com']});
+ await merged('https://jigsaw.w3.org/HTTP/300/301.html', {allowedHosts: ['google.com'], prefixUrl: ''});
// CancelError: Redirection to jigsaw.w3.org is not allowed
})();
```
diff --git a/migration-guides.md b/migration-guides.md
index 50263912c..f6c3d75d0 100644
--- a/migration-guides.md
+++ b/migration-guides.md
@@ -68,8 +68,6 @@ The [`timeout` option](https://github.com/sindresorhus/got#timeout) has some ext
The [`searchParams` option](https://github.com/sindresorhus/got#searchParams) is always serialized using [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) unless it's a `string`.
-The [`baseUrl` option](https://github.com/sindresorhus/got#baseurl) appends the ending slash if it's not present.
-
There's no `maxRedirects` option. It's always set to `10`.
To use streams, just call `got.stream(url, options)` or `got(url, {stream: true, ...}`).
@@ -82,6 +80,7 @@ To use streams, just call `got.stream(url, options)` or `got(url, {stream: true,
- No `agentClass`/`agentOptions`/`pool` option.
- No `forever` option. You need to use [forever-agent](https://github.com/request/forever-agent).
- No `proxy` option. You need to [pass a custom agent](readme.md#proxies).
+- No `baseUrl` option. Instead, there is `prefixUrl` which appends a trailing slash if not present. It will be always prepended unless `url` is an instance of URL.
- No `removeRefererHeader` option. You can remove the referer header in a [`beforeRequest` hook](https://github.com/sindresorhus/got#hooksbeforeRequest):
```js
diff --git a/readme.md b/readme.md
index 546615da4..617365af7 100644
--- a/readme.md
+++ b/readme.md
@@ -114,28 +114,23 @@ Type: `object`
Any of the [`https.request`](https://nodejs.org/api/https.html#https_https_request_options_callback) options.
-###### baseUrl
+###### prefixUrl
-Type: `string | object`
-
-When specified, `url` will be prepended by `baseUrl`.
-If you specify an absolute URL, it will skip the `baseUrl`.
+Type: `string | URL`
-Very useful when used with `got.extend()` to create niche-specific Got instances.
+When specified, `prefixUrl` will be prepended to `url`. The prefix can be any valid URL, either relative or absolute. A trailing slash `/` is optional, one will be added automatically, if needed, when joining `prefixUrl` and `url`. The `url` argument cannot start with a `/` when using this option.
-Can be a string or a [WHATWG `URL`](https://nodejs.org/api/url.html#url_class_url).
+Useful when used with `got.extend()` to create niche-specific Got-instances.
-Slash at the end of `baseUrl` and at the beginning of the `url` argument is optional:
+**Note:** `prefixUrl` will be ignored if the `url` argument is a URL instance.
```js
-await got('hello', {baseUrl: 'https://example.com/v1'});
-//=> 'https://example.com/v1/hello'
-
-await got('/hello', {baseUrl: 'https://example.com/v1/'});
-//=> 'https://example.com/v1/hello'
+const got = require('got');
-await got('/hello', {baseUrl: 'https://example.com/v1'});
-//=> 'https://example.com/v1/hello'
+(async () => {
+ await ky('unicorn', {prefixUrl: 'https://cats.com'});
+ //=> 'https://cats.com/unicorn'
+})();
```
###### headers
@@ -717,7 +712,7 @@ Configure a new `got` instance with default `options`. The `options` are merged
```js
const client = got.extend({
- baseUrl: 'https://example.com',
+ prefixUrl: 'https://example.com',
headers: {
'x-unicorn': 'rainbow'
}
@@ -735,7 +730,7 @@ client.get('/demo');
```js
(async () => {
const client = got.extend({
- baseUrl: 'httpbin.org',
+ prefixUrl: 'httpbin.org',
headers: {
'x-foo': 'bar'
}
@@ -1026,7 +1021,7 @@ const chain = new AWS.CredentialProviderChain();
// Create a Got instance to use relative paths and signed requests
const awsClient = got.extend({
- baseUrl: 'https://.execute-api..amazonaws.com//',
+ prefixUrl: 'https://.execute-api..amazonaws.com//',
hooks: {
beforeRequest: [
async options => {
@@ -1151,7 +1146,7 @@ Bear in mind; if you send an `if-modified-since` header and receive a `304 Not M
### Custom endpoints
-Use `got.extend()` to make it nicer to work with REST APIs. Especially if you use the `baseUrl` option.
+Use `got.extend()` to make it nicer to work with REST APIs. Especially if you use the `prefixUrl` option.
**Note:** Not to be confused with [`got.create()`](advanced-creation.md), which has no defaults.
@@ -1160,7 +1155,7 @@ const got = require('got');
const pkg = require('./package.json');
const custom = got.extend({
- baseUrl: 'example.com',
+ prefixUrl: 'example.com',
responseType: 'json',
headers: {
'user-agent': `my-package/${pkg.version} (https://github.com/username/my-package)`
diff --git a/source/normalize-arguments.ts b/source/normalize-arguments.ts
index bec64f170..e0e74944a 100644
--- a/source/normalize-arguments.ts
+++ b/source/normalize-arguments.ts
@@ -44,8 +44,12 @@ export const preNormalizeArguments = (options: Options, defaults?: Options): Nor
options.headers = lowercaseKeys(options.headers);
}
- if (options.baseUrl && !options.baseUrl.toString().endsWith('/')) {
- options.baseUrl += '/';
+ if (options.prefixUrl) {
+ options.prefixUrl = options.prefixUrl.toString();
+
+ if (!options.prefixUrl.toString().endsWith('/')) {
+ options.prefixUrl += '/';
+ }
}
if (is.nullOrUndefined(options.hooks)) {
@@ -125,7 +129,7 @@ export const normalizeArguments = (url: URLOrOptions, options: NormalizedOptions
let urlArgument: URLArgument;
if (is.plainObject(url)) {
options = {...url, ...options};
- urlArgument = options.url || '';
+ urlArgument = options.url || {};
delete options.url;
} else {
urlArgument = url;
@@ -143,17 +147,24 @@ export const normalizeArguments = (url: URLOrOptions, options: NormalizedOptions
let urlObj: https.RequestOptions | URLOptions;
if (is.string(urlArgument)) {
- if (options.baseUrl) {
- if (urlArgument.startsWith('/')) {
- urlArgument = urlArgument.slice(1);
- }
- } else {
- urlArgument = urlArgument.replace(/^unix:/, 'http://$&');
+ if (options.prefixUrl && urlArgument.startsWith('/')) {
+ throw new Error('`url` must not begin with a slash when using `prefixUrl`');
}
- urlObj = urlArgument || options.baseUrl ? urlToOptions(new URL(urlArgument, options.baseUrl)) : {};
+ if (options.prefixUrl) {
+ urlArgument = options.prefixUrl + urlArgument;
+ }
+
+ urlArgument = urlArgument.replace(/^unix:/, 'http://$&');
+
+ urlObj = urlToOptions(new URL(urlArgument));
} else if (is.urlInstance(urlArgument)) {
urlObj = urlToOptions(urlArgument);
+ } else if (options.prefixUrl) {
+ urlObj = {
+ ...urlToOptions(new URL(options.prefixUrl)),
+ ...urlArgument
+ };
} else {
urlObj = urlArgument;
}
@@ -169,12 +180,12 @@ export const normalizeArguments = (url: URLOrOptions, options: NormalizedOptions
}
}
- const {baseUrl} = options;
- Object.defineProperty(options, 'baseUrl', {
+ const {prefixUrl} = options;
+ Object.defineProperty(options, 'prefixUrl', {
set: () => {
- throw new Error('Failed to set baseUrl. Options are normalized already.');
+ throw new Error('Failed to set prefixUrl. Options are normalized already.');
},
- get: () => baseUrl
+ get: () => prefixUrl
});
let {searchParams} = options;
diff --git a/source/utils/types.ts b/source/utils/types.ts
index 5ba70f6ca..b07b55f05 100644
--- a/source/utils/types.ts
+++ b/source/utils/types.ts
@@ -134,7 +134,7 @@ export interface Options extends Omit | Keyv | false;
url?: URL | string;
@@ -151,7 +151,7 @@ export interface NormalizedOptions extends Omit, 'timeout' | '
gotTimeout: Required;
retry: NormalizedRetryOptions;
lookup?: CacheableLookup['lookup'];
- readonly baseUrl: string;
+ readonly prefixUrl: string;
path: string;
hostname: string;
host: string;
diff --git a/test/arguments.ts b/test/arguments.ts
index d52047828..1db6325c5 100644
--- a/test/arguments.ts
+++ b/test/arguments.ts
@@ -39,9 +39,9 @@ test('throws an error if the protocol is not specified', async t => {
test('string url with searchParams is preserved', withServer, async (t, server, got) => {
server.get('/', echoUrl);
- const path = '/?test=http://example.com?foo=bar';
+ const path = '?test=http://example.com?foo=bar';
const {body} = await got(path);
- t.is(body, path);
+ t.is(body, `/${path}`);
});
test('options are optional', withServer, async (t, server, got) => {
@@ -161,7 +161,7 @@ test('accepts `url` as an option', withServer, async (t, server, got) => {
await t.notThrowsAsync(got({url: 'test'}));
});
-test('can omit `url` option if using `baseUrl`', withServer, async (t, server, got) => {
+test('can omit `url` option if using `prefixUrl`', withServer, async (t, server, got) => {
server.get('/', echoUrl);
await t.notThrowsAsync(got({}));
@@ -206,49 +206,41 @@ test('allows extra keys in `options.hooks`', withServer, async (t, server, got)
await t.notThrowsAsync(got('test', {hooks: {extra: {}}}));
});
-test('`baseUrl` option works', withServer, async (t, server, got) => {
+test('`prefixUrl` option works', withServer, async (t, server, got) => {
server.get('/test/foobar', echoUrl);
- const instanceA = got.extend({baseUrl: `${server.url}/test`});
- const {body} = await instanceA('/foobar');
- t.is(body, '/test/foobar');
-});
-
-test('accepts WHATWG URL as the `baseUrl` option', withServer, async (t, server, got) => {
- server.get('/test/foobar', echoUrl);
-
- const instanceA = got.extend({baseUrl: new URL(`${server.url}/test`)});
- const {body} = await instanceA('/foobar');
+ const instanceA = got.extend({prefixUrl: `${server.url}/test`});
+ const {body} = await instanceA('foobar');
t.is(body, '/test/foobar');
});
-test('backslash in the end of `baseUrl` option is optional', withServer, async (t, server) => {
+test('accepts WHATWG URL as the `prefixUrl` option', withServer, async (t, server, got) => {
server.get('/test/foobar', echoUrl);
- const instanceA = got.extend({baseUrl: `${server.url}/test/`});
- const {body} = await instanceA('/foobar');
+ const instanceA = got.extend({prefixUrl: new URL(`${server.url}/test`)});
+ const {body} = await instanceA('foobar');
t.is(body, '/test/foobar');
});
-test('backslash in the beginning of `url` is optional when using `baseUrl` option', withServer, async (t, server) => {
+test('backslash in the end of `prefixUrl` option is optional', withServer, async (t, server) => {
server.get('/test/foobar', echoUrl);
- const instanceA = got.extend({baseUrl: `${server.url}/test`});
+ const instanceA = got.extend({prefixUrl: `${server.url}/test/`});
const {body} = await instanceA('foobar');
t.is(body, '/test/foobar');
});
-test('throws when trying to modify `baseUrl` after options got normalized', async t => {
+test('throws when trying to modify `prefixUrl` after options got normalized', async t => {
const instanceA = got.create({
methods: [],
- options: {baseUrl: 'https://example.com'},
+ options: {prefixUrl: 'https://example.com'},
handler: (options, next) => {
- options.baseUrl = 'https://google.com';
+ options.prefixUrl = 'https://google.com';
return next(options);
}
});
- await t.throwsAsync(instanceA('/'), 'Failed to set baseUrl. Options are normalized already.');
+ await t.throwsAsync(instanceA(''), 'Failed to set prefixUrl. Options are normalized already.');
});
test('throws if the `searchParams` value is invalid', async t => {
diff --git a/test/cache.ts b/test/cache.ts
index cb4498219..228b50d93 100644
--- a/test/cache.ts
+++ b/test/cache.ts
@@ -134,7 +134,7 @@ test('doesn\'t cache response when received HTTP error', withServer, async (t, s
test('DNS cache works', withServer, async (t, _server, got) => {
const map = new Map();
- await t.notThrowsAsync(got('https://example.com', {dnsCache: map}));
+ await t.notThrowsAsync(got('https://example.com', {dnsCache: map, prefixUrl: ''}));
t.is(map.size, 1);
});
diff --git a/test/create.ts b/test/create.ts
index 06fceea9e..8804985f7 100644
--- a/test/create.ts
+++ b/test/create.ts
@@ -95,9 +95,12 @@ test('extend keeps the old value if the new one is undefined', t => {
});
test('extend merges URL instances', t => {
- const a = got.extend({baseUrl: new URL('https://example.com')});
- const b = a.extend({baseUrl: '/foo'});
- t.is(b.defaults.options.baseUrl.toString(), 'https://example.com/foo/');
+ // @ts-ignore Custom instance.
+ const a = got.extend({custom: new URL('https://example.com')});
+ // @ts-ignore Custom instance.
+ const b = a.extend({custom: '/foo'});
+ // @ts-ignore Custom instance.
+ t.is(b.defaults.options.custom.toString(), 'https://example.com/foo');
});
test('create', withServer, async (t, server) => {
@@ -128,8 +131,8 @@ test('hooks are merged on got.extend()', t => {
test('custom endpoint with custom headers (extend)', withServer, async (t, server) => {
server.all('/', echoHeaders);
- const instance = got.extend({headers: {unicorn: 'rainbow'}, baseUrl: server.url});
- const headers = await instance('/').json();
+ const instance = got.extend({headers: {unicorn: 'rainbow'}, prefixUrl: server.url});
+ const headers = await instance('').json();
t.is(headers.unicorn, 'rainbow');
t.not(headers['user-agent'], undefined);
});
@@ -138,7 +141,7 @@ test('no tampering with defaults', t => {
const instance = got.create({
handler: got.defaults.handler,
options: got.mergeOptions(got.defaults.options, {
- baseUrl: 'example/'
+ prefixUrl: 'example/'
})
});
@@ -149,11 +152,11 @@ test('no tampering with defaults', t => {
// Tamper Time
t.throws(() => {
- instance.defaults.options.baseUrl = 'http://google.com';
+ instance.defaults.options.prefixUrl = 'http://google.com';
});
- t.is(instance.defaults.options.baseUrl, 'example/');
- t.is(instance2.defaults.options.baseUrl, 'example/');
+ t.is(instance.defaults.options.prefixUrl, 'example/');
+ t.is(instance2.defaults.options.prefixUrl, 'example/');
});
test('defaults can be mutable', t => {
diff --git a/test/helpers/with-server.ts b/test/helpers/with-server.ts
index 4fab383db..9e5f931b9 100644
--- a/test/helpers/with-server.ts
+++ b/test/helpers/with-server.ts
@@ -13,9 +13,9 @@ export default async (t, run) => {
});
// @ts-ignore Ignore errors for extending got, for the tests
- const preparedGot = got.extend({baseUrl: server.url, avaTest: t.title});
+ const preparedGot = got.extend({prefixUrl: server.url, avaTest: t.title});
// @ts-ignore Ignore errors for extending got, for the tests
- preparedGot.secure = got.extend({baseUrl: server.sslUrl, avaTest: t.title});
+ preparedGot.secure = got.extend({prefixUrl: server.sslUrl, avaTest: t.title});
server.hostname = (new URL(server.url)).hostname;
server.sslHostname = (new URL(server.sslUrl)).hostname;
diff --git a/test/hooks.ts b/test/hooks.ts
index c623e9218..6699a9655 100644
--- a/test/hooks.ts
+++ b/test/hooks.ts
@@ -487,7 +487,7 @@ test('beforeError allows modifications', async t => {
test('does not break on `afterResponse` hook with JSON mode', withServer, async (t, server, got) => {
server.get('/foobar', echoHeaders);
- await t.notThrowsAsync(got('/', {
+ await t.notThrowsAsync(got('', {
hooks: {
afterResponse: [
(response, retryWithMergedOptions) => {
diff --git a/test/merge-instances.ts b/test/merge-instances.ts
index 923a86ce5..f8bc02940 100644
--- a/test/merge-instances.ts
+++ b/test/merge-instances.ts
@@ -13,10 +13,10 @@ test('merging instances', withServer, async (t, server) => {
server.get('/', echoHeaders);
const instanceA = got.extend({headers: {unicorn: 'rainbow'}});
- const instanceB = got.extend({baseUrl: server.url});
+ const instanceB = got.extend({prefixUrl: server.url});
const merged = got.mergeInstances(instanceA, instanceB);
- const headers = await merged('/').json();
+ const headers = await merged('').json();
t.is(headers.unicorn, 'rainbow');
t.not(headers['user-agent'], undefined);
});
diff --git a/test/progress.ts b/test/progress.ts
index 2a0006cfe..676ccda8a 100644
--- a/test/progress.ts
+++ b/test/progress.ts
@@ -72,7 +72,7 @@ test('download progress - missing total size', withServer, async (t, server, got
const events = [];
- await got('/').on('downloadProgress', event => events.push(event));
+ await got('').on('downloadProgress', event => events.push(event));
checkEvents(t, events);
});
diff --git a/test/stream.ts b/test/stream.ts
index 635fcb2c2..206d26c54 100644
--- a/test/stream.ts
+++ b/test/stream.ts
@@ -106,7 +106,7 @@ test('has error event', withServer, async (t, server, got) => {
});
test('has error event #2', withServer, async (t, _server, got) => {
- const stream = got.stream('http://doesntexist');
+ const stream = got.stream('http://doesntexist', {prefixUrl: null});
await t.throwsAsync(pEvent(stream, 'response'), {code: 'ENOTFOUND'});
});
diff --git a/test/unix-socket.ts b/test/unix-socket.ts
index 9ae89d4c8..b31d0d278 100644
--- a/test/unix-socket.ts
+++ b/test/unix-socket.ts
@@ -3,29 +3,27 @@ import test from 'ava';
import got from '../source';
import {withSocketServer} from './helpers/with-server';
+const okHandler = (_request, response) => {
+ response.end('ok');
+};
+
if (process.platform !== 'win32') {
test('works', withSocketServer, async (t, server) => {
- server.on('/', (_request, response) => {
- response.end('ok');
- });
+ server.on('/', okHandler);
const url = format('http://unix:%s:%s', server.socketPath, '/');
t.is((await got(url)).body, 'ok');
});
test('protocol-less works', withSocketServer, async (t, server) => {
- server.on('/', (_request, response) => {
- response.end('ok');
- });
+ server.on('/', okHandler);
const url = format('unix:%s:%s', server.socketPath, '/');
t.is((await got(url)).body, 'ok');
});
test('address with : works', withSocketServer, async (t, server) => {
- server.on('/foo:bar', (_request, response) => {
- response.end('ok');
- });
+ server.on('/foo:bar', okHandler);
const url = format('unix:%s:%s', server.socketPath, '/foo:bar');
t.is((await got(url)).body, 'ok');
@@ -37,4 +35,12 @@ if (process.platform !== 'win32') {
code: 'ENOTFOUND'
});
});
+
+ test('works when extending instances', withSocketServer, async (t, server) => {
+ server.on('/', okHandler);
+
+ const url = format('unix:%s:%s', server.socketPath, '/');
+ const instance = got.extend({prefixUrl: url});
+ t.is((await instance('')).body, 'ok');
+ });
}