Skip to content

Commit

Permalink
Add skipEmptyString option (#252)
Browse files Browse the repository at this point in the history
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
  • Loading branch information
mum-never-proud and sindresorhus committed Apr 6, 2020
1 parent bed0871 commit 20233a6
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 9 deletions.
27 changes: 27 additions & 0 deletions index.d.ts
Expand Up @@ -283,6 +283,33 @@ export interface StringifyOptions {
```
*/
readonly skipNull?: boolean;

/**
Skip keys with an empty string as the value.
@default false
@example
```
import queryString = require('query-string');
queryString.stringify({a: 1, b: '', c: '', d: 4}, {
skipEmptyString: true
});
//=> 'a=1&d=4'
```
@example
```
import queryString = require('query-string');
queryString.stringify({a: '', b: ''}, {
skipEmptyString: true
});
//=> ''
```
*/
readonly skipEmptyString?: boolean;
}

/**
Expand Down
37 changes: 28 additions & 9 deletions index.js
Expand Up @@ -3,12 +3,19 @@ const strictUriEncode = require('strict-uri-encode');
const decodeComponent = require('decode-uri-component');
const splitOnFirst = require('split-on-first');

const isNullOrUndefined = value => value === null || value === undefined;

function encoderForArrayFormat(options) {
switch (options.arrayFormat) {
case 'index':
return key => (result, value) => {
const index = result.length;
if (value === undefined || (options.skipNull && value === null)) {

if (
value === undefined ||
(options.skipNull && value === null) ||
(options.skipEmptyString && value === '')
) {
return result;
}

Expand All @@ -24,7 +31,11 @@ function encoderForArrayFormat(options) {

case 'bracket':
return key => (result, value) => {
if (value === undefined || (options.skipNull && value === null)) {
if (
value === undefined ||
(options.skipNull && value === null) ||
(options.skipEmptyString && value === '')
) {
return result;
}

Expand All @@ -51,7 +62,11 @@ function encoderForArrayFormat(options) {

default:
return key => (result, value) => {
if (value === undefined || (options.skipNull && value === null)) {
if (
value === undefined ||
(options.skipNull && value === null) ||
(options.skipEmptyString && value === '')
) {
return result;
}

Expand Down Expand Up @@ -280,14 +295,18 @@ exports.stringify = (object, options) => {

validateArrayFormatSeparator(options.arrayFormatSeparator);

const shouldFilter = key => (
(options.skipNull && isNullOrUndefined(object[key])) ||
(options.skipEmptyString && object[key] === '')
);

const formatter = encoderForArrayFormat(options);

const objectCopy = Object.assign({}, object);
if (options.skipNull) {
for (const key of Object.keys(objectCopy)) {
if (objectCopy[key] === undefined || objectCopy[key] === null) {
delete objectCopy[key];
}
const objectCopy = {};

for (const key of Object.keys(object)) {
if (!shouldFilter(key)) {
objectCopy[key] = object[key];
}
}

Expand Down
1 change: 1 addition & 0 deletions index.test-d.ts
Expand Up @@ -23,6 +23,7 @@ expectType<string>(queryString.stringify({foo: 'bar'}, {arrayFormat: 'none'}));
expectType<string>(queryString.stringify({foo: 'bar'}, {arrayFormat: 'comma'}));
expectType<string>(queryString.stringify({foo: 'bar'}, {sort: false}));
expectType<string>(queryString.stringify({foo: 'bar'}, {skipNull: true}));
expectType<string>(queryString.stringify({foo: 'bar'}, {skipEmptyString: true}));
const order = ['c', 'a', 'b'];
expectType<string>(
queryString.stringify(
Expand Down
25 changes: 25 additions & 0 deletions readme.md
Expand Up @@ -304,6 +304,31 @@ queryString.stringify({a: undefined, b: null}, {
//=> ''
```

##### skipEmptyString

Skip keys with an empty string as the value.

Type: `boolean`\
Default: `false`

```js
const queryString = require('query-string');

queryString.stringify({a: 1, b: '', c: '', d: 4}, {
skipEmptyString: true
});
//=> 'a=1&d=4'
```

```js
const queryString = require('query-string');

queryString.stringify({a: '', b: ''}, {
skipEmptyString: true
});
//=> ''
```

### .extract(string)

Extract a query string from a URL that can be passed into `.parse()`.
Expand Down
7 changes: 7 additions & 0 deletions test/stringify-url.js
Expand Up @@ -19,6 +19,13 @@ test('stringify URL with a query string', t => {
t.deepEqual(queryString.stringifyUrl({url: 'https://foo.bar?foo=baz', query: {foo: 'bar'}}), 'https://foo.bar?foo=bar');
});

test('skipEmptyString:: stringify URL with a query string', t => {
const config = {skipEmptyString: true};

t.deepEqual(queryString.stringifyUrl({url: 'https://foo.bar', query: {foo: 'bar', baz: ''}}, config), 'https://foo.bar?foo=bar');
t.deepEqual(queryString.stringifyUrl({url: 'https://foo.bar', query: {foo: 'bar', baz: ['', 'qux']}}, config), 'https://foo.bar?baz=qux&foo=bar');
});

test('stringify URL from the result of `parseUrl` without query string', t => {
const url = 'https://foo.bar';
const parsedUrl = queryString.parseUrl(url);
Expand Down
54 changes: 54 additions & 0 deletions test/stringify.js
Expand Up @@ -204,6 +204,16 @@ test('should ignore null when skipNull is set', t => {
}), 'a=1&c=3');
});

test('should ignore emptyString when skipEmptyString is set', t => {
t.is(queryString.stringify({
a: 1,
b: '',
c: 3
}, {
skipEmptyString: true
}), 'a=1&c=3');
});

test('should ignore undefined when skipNull is set', t => {
t.is(queryString.stringify({
a: 1,
Expand Down Expand Up @@ -260,6 +270,50 @@ test('should ignore both null and undefined when skipNull is set for arrayFormat
}), 'a[0]=1&a[1]=2&c=1');
});

test('should ignore empty string when skipEmptyString is set for arrayFormat', t => {
t.is(queryString.stringify({
a: ['', 1, '', 2],
b: '',
c: 1
}, {
skipEmptyString: true
}), 'a=1&a=2&c=1');

t.is(queryString.stringify({
a: ['', 1, '', 2],
b: '',
c: 1
}, {
skipEmptyString: true,
arrayFormat: 'bracket'
}), 'a[]=1&a[]=2&c=1');

t.is(queryString.stringify({
a: ['', 1, '', 2],
b: '',
c: 1
}, {
skipEmptyString: true,
arrayFormat: 'comma'
}), 'a=1,2&c=1');

t.is(queryString.stringify({
a: ['', 1, '', 2],
b: '',
c: 1
}, {
skipEmptyString: true,
arrayFormat: 'index'
}), 'a[0]=1&a[1]=2&c=1');

t.is(queryString.stringify({
a: ['', '', '', ''],
c: 1
}, {
skipEmptyString: true
}), 'c=1');
});

test('stringify throws TypeError for invalid arrayFormatSeparator', t => {
t.throws(_ => queryString.stringify({}, {arrayFormatSeparator: ',,'}), {
instanceOf: TypeError
Expand Down

0 comments on commit 20233a6

Please sign in to comment.