Skip to content

Commit 1604958

Browse files
Add .stringifyUrl() method (#217)
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
1 parent d7c4b89 commit 1604958

4 files changed

Lines changed: 114 additions & 0 deletions

File tree

index.d.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,3 +267,22 @@ Extract a query string from a URL that can be passed into `.parse()`.
267267
Note: This behaviour can be changed with the `skipNull` option.
268268
*/
269269
export function extract(url: string): string;
270+
271+
/**
272+
Stringify an object into a URL with a query string and sorting the keys. The inverse of [`.parseUrl()`](https://github.com/sindresorhus/query-string#parseurlstring-options)
273+
274+
Query items in the `query` property overrides queries in the `url` property.
275+
276+
@example
277+
```
278+
queryString.stringifyUrl({url: 'https://foo.bar', query: {foo: 'bar'}});
279+
//=> 'https://foo.bar?foo=bar'
280+
281+
queryString.stringifyUrl({url: 'https://foo.bar?foo=baz', query: {foo: 'bar'}});
282+
//=> 'https://foo.bar?foo=bar'
283+
```
284+
*/
285+
export function stringifyUrl(
286+
object: ParsedUrl,
287+
options?: StringifyOptions
288+
): string;

index.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,16 @@ function removeHash(input) {
161161
return input;
162162
}
163163

164+
function getHash(url) {
165+
let hash = '';
166+
const hashStart = url.indexOf('#');
167+
if (hashStart !== -1) {
168+
hash = url.slice(hashStart);
169+
}
170+
171+
return hash;
172+
}
173+
164174
function extract(input) {
165175
input = removeHash(input);
166176
const queryStart = input.indexOf('?');
@@ -300,3 +310,17 @@ exports.parseUrl = (input, options) => {
300310
query: parse(extract(input), options)
301311
};
302312
};
313+
314+
exports.stringifyUrl = (input, options) => {
315+
const url = removeHash(input.url).split('?')[0] || '';
316+
const queryFromUrl = this.extract(input.url);
317+
const parsedQueryFromUrl = this.parse(queryFromUrl);
318+
const hash = getHash(input.url);
319+
const query = Object.assign(parsedQueryFromUrl, input.query);
320+
let queryString = this.stringify(query, options);
321+
if (queryString) {
322+
queryString = `?${queryString}`;
323+
}
324+
325+
return `${url}${queryString}${hash}`;
326+
};

readme.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,39 @@ queryString.parseUrl('https://foo.bar?foo=bar');
304304
//=> {url: 'https://foo.bar', query: {foo: 'bar'}}
305305
```
306306

307+
### .stringifyUrl(object, options?)
308+
309+
Stringify an object into a URL with a query string and sorting the keys. The inverse of [`.parseUrl()`](https://github.com/sindresorhus/query-string#parseurlstring-options)
310+
311+
The `options` are the same as for `.stringify()`.
312+
313+
Returns a string with the URL and a query string.
314+
315+
Query items in the `query` property overrides queries in the `url` property.
316+
317+
```js
318+
queryString.stringifyUrl({url: 'https://foo.bar', query: {foo: 'bar'}});
319+
//=> 'https://foo.bar?foo=bar'
320+
321+
queryString.stringifyUrl({url: 'https://foo.bar?foo=baz', query: {foo: 'bar'}});
322+
//=> 'https://foo.bar?foo=bar'
323+
```
324+
325+
#### object
326+
327+
Type: `object`
328+
329+
##### url
330+
331+
Type: `string`
332+
333+
The URL to stringify.
334+
335+
##### query
336+
337+
Type: `object`
338+
339+
Query items to add to the URL.
307340

308341
## Nesting
309342

test/stringify-url.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import test from 'ava';
2+
import queryString from '..';
3+
4+
test('stringify URL without a query string', t => {
5+
t.deepEqual(queryString.stringifyUrl({url: 'https://foo.bar/'}), 'https://foo.bar/');
6+
t.deepEqual(queryString.stringifyUrl({url: 'https://foo.bar/', query: {}}), 'https://foo.bar/');
7+
t.deepEqual(queryString.stringifyUrl({url: 'https://foo.bar/#top', query: {}}), 'https://foo.bar/#top');
8+
t.deepEqual(queryString.stringifyUrl({url: '', query: {}}), '');
9+
t.deepEqual(queryString.stringifyUrl({url: 'https://foo.bar?', query: {}}), 'https://foo.bar');
10+
t.deepEqual(queryString.stringifyUrl({url: 'https://foo.bar?foo=bar', query: {}}), 'https://foo.bar?foo=bar');
11+
});
12+
13+
test('stringify URL with a query string', t => {
14+
t.deepEqual(queryString.stringifyUrl({url: 'https://foo.bar', query: {foo: 'bar'}}), 'https://foo.bar?foo=bar');
15+
t.deepEqual(queryString.stringifyUrl({url: 'https://foo.bar?', query: {foo: 'bar'}}), 'https://foo.bar?foo=bar');
16+
t.deepEqual(queryString.stringifyUrl({url: 'https://foo.bar/#top', query: {foo: 'bar'}}), 'https://foo.bar/?foo=bar#top');
17+
t.deepEqual(queryString.stringifyUrl({url: 'https://foo.bar', query: {foo: 'bar', a: 'b'}}), 'https://foo.bar?a=b&foo=bar');
18+
t.deepEqual(queryString.stringifyUrl({url: 'https://foo.bar?a=b', query: {foo: ['bar', 'baz']}}), 'https://foo.bar?a=b&foo=bar&foo=baz');
19+
t.deepEqual(queryString.stringifyUrl({url: 'https://foo.bar?foo=baz', query: {foo: 'bar'}}), 'https://foo.bar?foo=bar');
20+
});
21+
22+
test('stringify URL from the result of `parseUrl` without query string', t => {
23+
const url = 'https://foo.bar';
24+
const parsedUrl = queryString.parseUrl(url);
25+
t.deepEqual(queryString.stringifyUrl(parsedUrl), url);
26+
});
27+
28+
test('stringify URL from the result of `parseUrl` with query string', t => {
29+
const url = 'https://foo.bar?foo=bar&foo=baz';
30+
const parsedUrl = queryString.parseUrl(url);
31+
t.deepEqual(queryString.stringifyUrl(parsedUrl), url);
32+
});
33+
34+
test('stringify URL from the result of `parseUrl` with query string that contains `=`', t => {
35+
const url = 'https://foo.bar?foo=bar=&foo=baz=';
36+
const parsedUrl = queryString.parseUrl(url);
37+
t.deepEqual(queryString.stringifyUrl(parsedUrl, {encode: false}), url);
38+
});

0 commit comments

Comments
 (0)