Skip to content

Commit 17ca389

Browse files
authored
Don't produce URL with "?" alone (#706)
1 parent 0630c2c commit 17ca389

File tree

3 files changed

+58
-3
lines changed

3 files changed

+58
-3
lines changed

source/core/Ky.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {normalizeRequestMethod, normalizeRetryOptions} from '../utils/normalize.
1414
import timeout, {type TimeoutOptions} from '../utils/timeout.js';
1515
import delay from '../utils/delay.js';
1616
import {type ObjectEntries} from '../utils/types.js';
17-
import {findUnknownOptions} from '../utils/options.js';
17+
import {findUnknownOptions, hasSearchParameters} from '../utils/options.js';
1818
import {
1919
maxSafeTimeout,
2020
responseTypes,
@@ -187,7 +187,7 @@ export class Ky {
187187

188188
this.request = new globalThis.Request(this._input, this._options);
189189

190-
if (this._options.searchParams) {
190+
if (hasSearchParameters(this._options.searchParams)) {
191191
// eslint-disable-next-line unicorn/prevent-abbreviations
192192
const textSearchParams = typeof this._options.searchParams === 'string'
193193
? this._options.searchParams.replace(/^\?/, '')

source/utils/options.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {kyOptionKeys, requestOptionsRegistry} from '../core/constants.js';
2+
import type {SearchParamsOption} from '../types/options.js';
23

34
export const findUnknownOptions = (
45
request: Request,
@@ -14,3 +15,29 @@ export const findUnknownOptions = (
1415

1516
return unknownOptions;
1617
};
18+
19+
export const hasSearchParameters = (search: SearchParamsOption): boolean => {
20+
if (search === undefined) {
21+
return false;
22+
}
23+
24+
// The `typeof array` still gives "object", so we need different checking for array.
25+
if (Array.isArray(search)) {
26+
return search.length > 0;
27+
}
28+
29+
if (search instanceof URLSearchParams) {
30+
return search.size > 0;
31+
}
32+
33+
// Record
34+
if (typeof search === 'object') {
35+
return Object.keys(search).length > 0;
36+
}
37+
38+
if (typeof search === 'string') {
39+
return search.trim().length > 0;
40+
}
41+
42+
return Boolean(search);
43+
};

test/fetch.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import ky from '../source/index.js';
44
const fixture = 'https://example.com/unicorn';
55

66
test('fetch option takes a custom fetch function', async t => {
7-
t.plan(6);
7+
t.plan(10);
88

99
const customFetch: typeof fetch = async input => {
1010
if (!(input instanceof Request)) {
@@ -22,6 +22,34 @@ test('fetch option takes a custom fetch function', async t => {
2222
}).text(),
2323
`${fixture}?foo=bar`,
2424
);
25+
t.is(
26+
await ky(fixture, {
27+
fetch: customFetch,
28+
searchParams: {},
29+
}).text(),
30+
`${fixture}`,
31+
);
32+
t.is(
33+
await ky(fixture, {
34+
fetch: customFetch,
35+
searchParams: [],
36+
}).text(),
37+
`${fixture}`,
38+
);
39+
t.is(
40+
await ky(fixture, {
41+
fetch: customFetch,
42+
searchParams: new URLSearchParams(),
43+
}).text(),
44+
`${fixture}`,
45+
);
46+
t.is(
47+
await ky(fixture, {
48+
fetch: customFetch,
49+
searchParams: ' ',
50+
}).text(),
51+
`${fixture}`,
52+
);
2553
t.is(
2654
await ky(`${fixture}#hash`, {
2755
fetch: customFetch,

0 commit comments

Comments
 (0)