Repository: sindresorhus/query-string
Pull request #304 (2021-01-10 01:44:37)

Implement skips for stringify array format comma
Fixes https://github.com/sindresorhus/query-string/issues/302

sindresorhus: Is this PR done and ready to be re-reviewed?
decimoseptimo: Yes it is.

Patch:
diff --git a/index.d.ts b/index.d.ts
index 4a115fb..035ded8 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -229,6 +229,11 @@ export interface StringifyOptions {
 
 		queryString.stringify({foo: [1, 2, 3]}, {arrayFormat: 'comma'});
 		//=> 'foo=1,2,3'
+
+		queryString.stringify({foo: [1, null, '']}, {arrayFormat: 'comma'});
+		//=> 'foo=1,,'
+		// Note that typing information for null values is lost
+		// and `.parse('foo=1,,')` would return `{foo: [1, '', '']}`.
 		```
 
   - `separator`: Serialize arrays by separating elements with character:
diff --git a/index.js b/index.js
index 423b9d6..07764d5 100644
--- a/index.js
+++ b/index.js
@@ -50,12 +50,20 @@ function encoderForArrayFormat(options) {
 		case 'comma':
 		case 'separator':
 			return key => (result, value) => {
-				if (value === null || value === undefined || value.length === 0) {
+				if (
+					value === undefined ||
+					(options.skipNull && value === null) ||
+					(options.skipEmptyString && value === '')
+				) {
 					return result;
 				}
 
 				if (result.length === 0) {
-					return [[encode(key, options), '=', encode(value, options)].join('')];
+					return [[encode(key, options), '=', encode(value === null ? '' : value, options)].join('')];
+				}
+
+				if (value === null || value === '') {
+					return [[result, ''].join(options.arrayFormatSeparator)];
 				}
 
 				return [[result, encode(value, options)].join(options.arrayFormatSeparator)];
diff --git a/readme.md b/readme.md
index 884b0fd..f90aba6 100644
--- a/readme.md
+++ b/readme.md
@@ -241,6 +241,11 @@ const queryString = require('query-string');
 
 queryString.stringify({foo: [1, 2, 3]}, {arrayFormat: 'comma'});
 //=> 'foo=1,2,3'
+
+queryString.stringify({foo: [1, null, '']}, {arrayFormat: 'comma'});
+//=> 'foo=1,,'
+// Note that typing information for null values is lost
+// and `.parse('foo=1,,')` would return `{foo: [1, '', '']}`.
 ```
 
 - `'none'`: Serialize arrays by using duplicate keys:


Test patch:
diff --git a/test/parse.js b/test/parse.js
index 9bfc034..70759f1 100644
--- a/test/parse.js
+++ b/test/parse.js
@@ -218,7 +218,7 @@ test('query strings having ordered index arrays and format option as `index`', t
 	}), {bat: 'buz', foo: ['zero', 'two', 'one', 'three']});
 });
 
-test('circuit parse -> stringify', t => {
+test('circuit parse → stringify', t => {
 	const original = 'foo[3]=foo&foo[2]&foo[1]=one&foo[0]=&bat=buz';
 	const sortedOriginal = 'bat=buz&foo[0]=&foo[1]=one&foo[2]&foo[3]=foo';
 	const expected = {bat: 'buz', foo: ['', 'one', null, 'foo']};
@@ -231,7 +231,7 @@ test('circuit parse -> stringify', t => {
 	t.is(queryString.stringify(expected, options), sortedOriginal);
 });
 
-test('circuit original -> parse - > stringify -> sorted original', t => {
+test('circuit original → parse → stringify → sorted original', t => {
 	const original = 'foo[21474836471]=foo&foo[21474836470]&foo[1]=one&foo[0]=&bat=buz';
 	const sortedOriginal = 'bat=buz&foo[0]=&foo[1]=one&foo[2]&foo[3]=foo';
 	const options = {
@@ -241,6 +241,33 @@ test('circuit original -> parse - > stringify -> sorted original', t => {
 	t.deepEqual(queryString.stringify(queryString.parse(original, options), options), sortedOriginal);
 });
 
+test('circuit parse → stringify with array commas', t => {
+	const original = 'c=,a,,&b=&a=';
+	const sortedOriginal = 'a=&b=&c=,a,,';
+	const expected = {
+		c: ['', 'a', '', ''],
+		b: '',
+		a: ''
+	};
+	const options = {
+		arrayFormat: 'comma'
+	};
+
+	t.deepEqual(queryString.parse(original, options), expected);
+
+	t.is(queryString.stringify(expected, options), sortedOriginal);
+});
+
+test('circuit original → parse → stringify with array commas → sorted original', t => {
+	const original = 'c=,a,,&b=&a=';
+	const sortedOriginal = 'a=&b=&c=,a,,';
+	const options = {
+		arrayFormat: 'comma'
+	};
+
+	t.deepEqual(queryString.stringify(queryString.parse(original, options), options), sortedOriginal);
+});
+
 test('decode keys and values', t => {
 	t.deepEqual(queryString.parse('st%C3%A5le=foo'), {ståle: 'foo'});
 	t.deepEqual(queryString.parse('foo=%7B%ab%%7C%de%%7D+%%7Bst%C3%A5le%7D%'), {foo: '{%ab%|%de%} %{ståle}%'});
diff --git a/test/stringify.js b/test/stringify.js
index 5c3487b..4b8bda5 100644
--- a/test/stringify.js
+++ b/test/stringify.js
@@ -126,13 +126,26 @@ test('array stringify representation with array commas', t => {
 	}), 'bar=one,two&foo');
 });
 
-test('array stringify representation with array commas and null value', t => {
+test('array stringify representation with array commas, null & empty string', t => {
 	t.is(queryString.stringify({
-		foo: [null, 'a', null, ''],
-		bar: [null]
+		c: [null, 'a', '', null],
+		b: [null],
+		a: ['']
+	}, {
+		arrayFormat: 'comma'
+	}), 'a=&b=&c=,a,,');
+});
+
+test('array stringify representation with array commas, null & empty string (skip both)', t => {
+	t.is(queryString.stringify({
+		c: [null, 'a', '', null],
+		b: [null],
+		a: ['']
 	}, {
+		skipNull: true,
+		skipEmptyString: true,
 		arrayFormat: 'comma'
-	}), 'foo=a');
+	}), 'c=a');
 });
 
 test('array stringify representation with array commas and 0 value', t => {
@@ -141,7 +154,7 @@ test('array stringify representation with array commas and 0 value', t => {
 		bar: [null]
 	}, {
 		arrayFormat: 'comma'
-	}), 'foo=a,0');
+	}), 'bar=&foo=a,,0');
 });
 
 test('array stringify representation with a bad array format', t => {