From a6d4a3f480b2810a8cce3c0118a2aacc6c6c7add Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Fri, 26 Feb 2021 18:15:35 +0700 Subject: [PATCH 01/12] Fix TypeScript type for `stringifyUrl()` Fixes #308 --- index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index b6d651b..4a115fb 100644 --- a/index.d.ts +++ b/index.d.ts @@ -372,7 +372,7 @@ export interface UrlObject { /** Overrides queries in the `url` property. */ - readonly query: StringifiableRecord; + readonly query?: StringifiableRecord; /** Overrides the fragment identifier in the `url` property. From 0090a3418253eea4b2c437ba034dd445361325b2 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Fri, 26 Feb 2021 18:16:40 +0700 Subject: [PATCH 02/12] 6.14.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3b90b26..c75f01a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "query-string", - "version": "6.14.0", + "version": "6.14.1", "description": "Parse and stringify URL query strings", "license": "MIT", "repository": "sindresorhus/query-string", From 44abc66628199bd3766ac471e265e64206f146c9 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Sat, 13 Mar 2021 00:54:58 +0700 Subject: [PATCH 03/12] Meta tweaks --- readme.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 280972e..884b0fd 100644 --- a/readme.md +++ b/readme.md @@ -17,7 +17,20 @@

- + + +
+
+ +
+ Doppler +
+ All your environment variables, in one place +
+ Stop struggling with scattered API keys, hacking together home-brewed tools, +
+ and avoiding access controls. Keep your team and servers in sync with Doppler. +

From 828f032306216f09a17d900fb765b002e1fd7691 Mon Sep 17 00:00:00 2001 From: Miguel Valenzuela Date: Tue, 16 Mar 2021 22:56:31 -0700 Subject: [PATCH 04/12] Implement skips for stringify array format comma (#304) Co-authored-by: Sindre Sorhus --- index.d.ts | 5 +++++ index.js | 12 ++++++++++-- readme.md | 5 +++++ test/parse.js | 31 +++++++++++++++++++++++++++++-- test/stringify.js | 23 ++++++++++++++++++----- 5 files changed, 67 insertions(+), 9 deletions(-) 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: 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 => { From b10bc19699a09580c055a381541e726dfd01a001 Mon Sep 17 00:00:00 2001 From: Austin Keener Date: Thu, 18 Mar 2021 07:52:12 -0400 Subject: [PATCH 05/12] Add support for `arrayFormat: 'bracket-separator'` (#276) Co-authored-by: Sindre Sorhus --- benchmark.js | 5 +++- index.d.ts | 57 ++++++++++++++++++++++++++++++++++++++--- index.js | 43 ++++++++++++++++++++++++++----- readme.md | 60 +++++++++++++++++++++++++++++++++++++++++++ test/parse.js | 30 ++++++++++++++++++++++ test/stringify.js | 65 +++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 250 insertions(+), 10 deletions(-) diff --git a/benchmark.js b/benchmark.js index af120ea..b152460 100644 --- a/benchmark.js +++ b/benchmark.js @@ -20,6 +20,7 @@ const TEST_STRING = stringify(TEST_OBJECT); const TEST_BRACKETS_STRING = stringify(TEST_OBJECT, {arrayFormat: 'bracket'}); const TEST_INDEX_STRING = stringify(TEST_OBJECT, {arrayFormat: 'index'}); const TEST_COMMA_STRING = stringify(TEST_OBJECT, {arrayFormat: 'comma'}); +const TEST_BRACKET_SEPARATOR_STRING = stringify(TEST_OBJECT, {arrayFormat: 'bracket-separator'}); const TEST_URL = stringifyUrl({url: TEST_HOST, query: TEST_OBJECT}); // Creates a test case and adds it to the suite @@ -41,6 +42,7 @@ defineTestCase('parse', TEST_STRING, {decode: false}); defineTestCase('parse', TEST_BRACKETS_STRING, {arrayFormat: 'bracket'}); defineTestCase('parse', TEST_INDEX_STRING, {arrayFormat: 'index'}); defineTestCase('parse', TEST_COMMA_STRING, {arrayFormat: 'comma'}); +defineTestCase('parse', TEST_BRACKET_SEPARATOR_STRING, {arrayFormat: 'bracket-separator'}); // Stringify defineTestCase('stringify', TEST_OBJECT); @@ -51,6 +53,7 @@ defineTestCase('stringify', TEST_OBJECT, {skipEmptyString: true}); defineTestCase('stringify', TEST_OBJECT, {arrayFormat: 'bracket'}); defineTestCase('stringify', TEST_OBJECT, {arrayFormat: 'index'}); defineTestCase('stringify', TEST_OBJECT, {arrayFormat: 'comma'}); +defineTestCase('stringify', TEST_OBJECT, {arrayFormat: 'bracket-separator'}); // Extract defineTestCase('extract', TEST_URL); @@ -66,7 +69,7 @@ suite.on('cycle', event => { const {name, hz} = event.target; const opsPerSec = Math.round(hz).toLocaleString(); - console.log(name.padEnd(36, '_') + opsPerSec.padStart(12, '_') + ' ops/s'); + console.log(name.padEnd(46, '_') + opsPerSec.padStart(3, '_') + ' ops/s'); }); suite.run(); diff --git a/index.d.ts b/index.d.ts index 035ded8..847336d 100644 --- a/index.d.ts +++ b/index.d.ts @@ -45,6 +45,30 @@ export interface ParseOptions { //=> {foo: ['1', '2', '3']} ``` + - `bracket-separator`: Parse arrays (that are explicitly marked with brackets) with elements separated by a custom character: + + ``` + import queryString = require('query-string'); + + queryString.parse('foo[]', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'}); + //=> {foo: []} + + queryString.parse('foo[]=', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'}); + //=> {foo: ['']} + + queryString.parse('foo[]=1', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'}); + //=> {foo: ['1']} + + queryString.parse('foo[]=1|2|3', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'}); + //=> {foo: ['1', '2', '3']} + + queryString.parse('foo[]=1||3|||6', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'}); + //=> {foo: ['1', '', 3, '', '', '6']} + + queryString.parse('foo[]=1|2|3&bar=fluffy&baz[]=4', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'}); + //=> {foo: ['1', '2', '3'], bar: 'fluffy', baz:['4']} + ``` + - `none`: Parse arrays with elements using duplicate keys: ``` @@ -54,7 +78,7 @@ export interface ParseOptions { //=> {foo: ['1', '2', '3']} ``` */ - readonly arrayFormat?: 'bracket' | 'index' | 'comma' | 'separator' | 'none'; + readonly arrayFormat?: 'bracket' | 'index' | 'comma' | 'separator' | 'bracket-separator' | 'none'; /** The character used to separate array elements when using `{arrayFormat: 'separator'}`. @@ -236,7 +260,7 @@ export interface StringifyOptions { // and `.parse('foo=1,,')` would return `{foo: [1, '', '']}`. ``` - - `separator`: Serialize arrays by separating elements with character: + - `separator`: Serialize arrays by separating elements with character: ``` import queryString = require('query-string'); @@ -245,6 +269,33 @@ export interface StringifyOptions { //=> 'foo=1|2|3' ``` + - `bracket-separator`: Serialize arrays by explicitly post-fixing array names with brackets and separating elements with a custom character: + + ``` + import queryString = require('query-string'); + + queryString.stringify({foo: []}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'}); + //=> 'foo[]' + + queryString.stringify({foo: ['']}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'}); + //=> 'foo[]=' + + queryString.stringify({foo: [1]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'}); + //=> 'foo[]=1' + + queryString.stringify({foo: [1, 2, 3]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'}); + //=> 'foo[]=1|2|3' + + queryString.stringify({foo: [1, '', 3, null, null, 6]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'}); + //=> 'foo[]=1||3|||6' + + queryString.stringify({foo: [1, '', 3, null, null, 6]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|', skipNull: true}); + //=> 'foo[]=1||3|6' + + queryString.stringify({foo: [1, 2, 3], bar: 'fluffy', baz: [4]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'}); + //=> 'foo[]=1|2|3&bar=fluffy&baz[]=4' + ``` + - `none`: Serialize arrays by using duplicate keys: ``` @@ -254,7 +305,7 @@ export interface StringifyOptions { //=> 'foo=1&foo=2&foo=3' ``` */ - readonly arrayFormat?: 'bracket' | 'index' | 'comma' | 'separator' | 'none'; + readonly arrayFormat?: 'bracket' | 'index' | 'comma' | 'separator' | 'bracket-separator' | 'none'; /** The character used to separate array elements when using `{arrayFormat: 'separator'}`. diff --git a/index.js b/index.js index 07764d5..7ab5d92 100644 --- a/index.js +++ b/index.js @@ -49,6 +49,11 @@ function encoderForArrayFormat(options) { case 'comma': case 'separator': + case 'bracket-separator': { + const keyValueSep = options.arrayFormat === 'bracket-separator' ? + '[]=' : + '='; + return key => (result, value) => { if ( value === undefined || @@ -58,16 +63,16 @@ function encoderForArrayFormat(options) { return result; } - if (result.length === 0) { - return [[encode(key, options), '=', encode(value === null ? '' : value, options)].join('')]; - } + // Translate null to an empty string so that it doesn't serialize as 'null' + value = value === null ? '' : value; - if (value === null || value === '') { - return [[result, ''].join(options.arrayFormatSeparator)]; + if (result.length === 0) { + return [[encode(key, options), keyValueSep, encode(value, options)].join('')]; } return [[result, encode(value, options)].join(options.arrayFormatSeparator)]; }; + } default: return key => (result, value) => { @@ -138,6 +143,28 @@ function parserForArrayFormat(options) { accumulator[key] = newValue; }; + case 'bracket-separator': + return (key, value, accumulator) => { + const isArray = /(\[\])$/.test(key); + key = key.replace(/\[\]$/, ''); + + if (!isArray) { + accumulator[key] = value ? decode(value, options) : value; + return; + } + + const arrayValue = value === null ? + [] : + value.split(options.arrayFormatSeparator).map(item => decode(item, options)); + + if (accumulator[key] === undefined) { + accumulator[key] = arrayValue; + return; + } + + accumulator[key] = [].concat(accumulator[key], arrayValue); + }; + default: return (key, value, accumulator) => { if (accumulator[key] === undefined) { @@ -261,7 +288,7 @@ function parse(query, options) { // Missing `=` should be `null`: // http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters - value = value === undefined ? null : ['comma', 'separator'].includes(options.arrayFormat) ? value : decode(value, options); + value = value === undefined ? null : ['comma', 'separator', 'bracket-separator'].includes(options.arrayFormat) ? value : decode(value, options); formatter(decode(key, options), value, ret); } @@ -343,6 +370,10 @@ exports.stringify = (object, options) => { } if (Array.isArray(value)) { + if (value.length === 0 && options.arrayFormat === 'bracket-separator') { + return encode(key, options) + '[]'; + } + return value .reduce(formatter(key), []) .join('&'); diff --git a/readme.md b/readme.md index f90aba6..600a971 100644 --- a/readme.md +++ b/readme.md @@ -138,6 +138,30 @@ queryString.parse('foo=1|2|3', {arrayFormat: 'separator', arrayFormatSeparator: //=> {foo: ['1', '2', '3']} ``` +- `'bracket-separator'`: Parse arrays (that are explicitly marked with brackets) with elements separated by a custom character: + +```js +const queryString = require('query-string'); + +queryString.parse('foo[]', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'}); +//=> {foo: []} + +queryString.parse('foo[]=', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'}); +//=> {foo: ['']} + +queryString.parse('foo[]=1', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'}); +//=> {foo: ['1']} + +queryString.parse('foo[]=1|2|3', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'}); +//=> {foo: ['1', '2', '3']} + +queryString.parse('foo[]=1||3|||6', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'}); +//=> {foo: ['1', '', 3, '', '', '6']} + +queryString.parse('foo[]=1|2|3&bar=fluffy&baz[]=4', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'}); +//=> {foo: ['1', '2', '3'], bar: 'fluffy', baz:['4']} +``` + - `'none'`: Parse arrays with elements using duplicate keys: ```js @@ -248,6 +272,42 @@ queryString.stringify({foo: [1, null, '']}, {arrayFormat: 'comma'}); // and `.parse('foo=1,,')` would return `{foo: [1, '', '']}`. ``` +- `'separator'`: Serialize arrays by separating elements with a custom character: + +```js +const queryString = require('query-string'); + +queryString.stringify({foo: [1, 2, 3]}, {arrayFormat: 'separator', arrayFormatSeparator: '|'}); +//=> 'foo=1|2|3' +``` + +- `'bracket-separator'`: Serialize arrays by explicitly post-fixing array names with brackets and separating elements with a custom character: + +```js +const queryString = require('query-string'); + +queryString.stringify({foo: []}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'}); +//=> 'foo[]' + +queryString.stringify({foo: ['']}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'}); +//=> 'foo[]=' + +queryString.stringify({foo: [1]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'}); +//=> 'foo[]=1' + +queryString.stringify({foo: [1, 2, 3]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'}); +//=> 'foo[]=1|2|3' + +queryString.stringify({foo: [1, '', 3, null, null, 6]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'}); +//=> 'foo[]=1||3|||6' + +queryString.stringify({foo: [1, '', 3, null, null, 6]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|', skipNull: true}); +//=> 'foo[]=1||3|6' + +queryString.stringify({foo: [1, 2, 3], bar: 'fluffy', baz: [4]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'}); +//=> 'foo[]=1|2|3&bar=fluffy&baz[]=4' +``` + - `'none'`: Serialize arrays by using duplicate keys: ```js diff --git a/test/parse.js b/test/parse.js index 70759f1..731a848 100644 --- a/test/parse.js +++ b/test/parse.js @@ -184,6 +184,36 @@ test('query strings having indexed arrays and format option as `index`', t => { }), {foo: ['bar', 'baz']}); }); +test('query strings having brackets+separator arrays and format option as `bracket-separator` with 1 value', t => { + t.deepEqual(queryString.parse('foo[]=bar', { + arrayFormat: 'bracket-separator' + }), {foo: ['bar']}); +}); + +test('query strings having brackets+separator arrays and format option as `bracket-separator` with multiple values', t => { + t.deepEqual(queryString.parse('foo[]=bar,baz,,,biz', { + arrayFormat: 'bracket-separator' + }), {foo: ['bar', 'baz', '', '', 'biz']}); +}); + +test('query strings with multiple brackets+separator arrays and format option as `bracket-separator` using same key name', t => { + t.deepEqual(queryString.parse('foo[]=bar,baz&foo[]=biz,boz', { + arrayFormat: 'bracket-separator' + }), {foo: ['bar', 'baz', 'biz', 'boz']}); +}); + +test('query strings having an empty brackets+separator array and format option as `bracket-separator`', t => { + t.deepEqual(queryString.parse('foo[]', { + arrayFormat: 'bracket-separator' + }), {foo: []}); +}); + +test('query strings having a brackets+separator array and format option as `bracket-separator` with a single empty string', t => { + t.deepEqual(queryString.parse('foo[]=', { + arrayFormat: 'bracket-separator' + }), {foo: ['']}); +}); + test('query strings having = within parameters (i.e. GraphQL IDs)', t => { t.deepEqual(queryString.parse('foo=bar=&foo=ba=z='), {foo: ['bar=', 'ba=z=']}); }); diff --git a/test/stringify.js b/test/stringify.js index 4b8bda5..c8751eb 100644 --- a/test/stringify.js +++ b/test/stringify.js @@ -172,6 +172,71 @@ test('array stringify representation with array indexes and sparse array', t => t.is(queryString.stringify({bar: fixture}, {arrayFormat: 'index'}), 'bar[0]=one&bar[1]=two&bar[2]=three'); }); +test('array stringify representation with brackets and separators with empty array', t => { + t.is(queryString.stringify({ + foo: null, + bar: [] + }, { + arrayFormat: 'bracket-separator' + }), 'bar[]&foo'); +}); + +test('array stringify representation with brackets and separators with single value', t => { + t.is(queryString.stringify({ + foo: null, + bar: ['one'] + }, { + arrayFormat: 'bracket-separator' + }), 'bar[]=one&foo'); +}); + +test('array stringify representation with brackets and separators with multiple values', t => { + t.is(queryString.stringify({ + foo: null, + bar: ['one', 'two', 'three'] + }, { + arrayFormat: 'bracket-separator' + }), 'bar[]=one,two,three&foo'); +}); + +test('array stringify representation with brackets and separators with a single empty string', t => { + t.is(queryString.stringify({ + foo: null, + bar: [''] + }, { + arrayFormat: 'bracket-separator' + }), 'bar[]=&foo'); +}); + +test('array stringify representation with brackets and separators with a multiple empty string', t => { + t.is(queryString.stringify({ + foo: null, + bar: ['', 'two', ''] + }, { + arrayFormat: 'bracket-separator' + }), 'bar[]=,two,&foo'); +}); + +test('array stringify representation with brackets and separators with dropped empty strings', t => { + t.is(queryString.stringify({ + foo: null, + bar: ['', 'two', ''] + }, { + arrayFormat: 'bracket-separator', + skipEmptyString: true + }), 'bar[]=two&foo'); +}); + +test('array stringify representation with brackets and separators with dropped null values', t => { + t.is(queryString.stringify({ + foo: null, + bar: ['one', null, 'three', null, '', 'six'] + }, { + arrayFormat: 'bracket-separator', + skipNull: true + }), 'bar[]=one,three,,six'); +}); + test('should sort keys in given order', t => { const fixture = ['c', 'a', 'b']; const sort = (key1, key2) => fixture.indexOf(key1) - fixture.indexOf(key2); From 20992772758fc74188db64e789535469adaf241f Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Thu, 18 Mar 2021 18:53:55 +0700 Subject: [PATCH 06/12] 7.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c75f01a..2680bc1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "query-string", - "version": "6.14.1", + "version": "7.0.0", "description": "Parse and stringify URL query strings", "license": "MIT", "repository": "sindresorhus/query-string", From bc6b4785b123aa97d5bb44876356b9a1069798f6 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Fri, 21 May 2021 19:05:11 +0700 Subject: [PATCH 07/12] Meta tweaks --- readme.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/readme.md b/readme.md index 600a971..68a8b6a 100644 --- a/readme.md +++ b/readme.md @@ -21,6 +21,7 @@

+
Doppler @@ -32,6 +33,16 @@ and avoiding access controls. Keep your team and servers in sync with Doppler.
+
+ +
+ Strapi +
+ Strapi is the leading open-source headless CMS. +
+ It’s 100% JavaScript, fully customizable, and developer-first. +
+

From 9e2482d458a5fc4a997fa9f1558d8a0daeeaa512 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Wed, 26 May 2021 00:04:51 +0700 Subject: [PATCH 08/12] Update readme.md --- readme.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 68a8b6a..67bd4bc 100644 --- a/readme.md +++ b/readme.md @@ -56,7 +56,9 @@ $ npm install query-string ``` -This module targets Node.js 6 or later and the latest version of Chrome, Firefox, and Safari. If you want support for older browsers, or, if your project is using create-react-app v1, use version 5: `npm install query-string@5`. +**Not `npm install querystring`!!!!!** + +This module targets Node.js 6 or later and the latest version of Chrome, Firefox, and Safari. ## Usage From 4279ef880c0d4fbbed60c3149bed73298800daa2 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Wed, 9 Jun 2021 01:11:53 +0700 Subject: [PATCH 09/12] Meta tweaks --- readme.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/readme.md b/readme.md index 67bd4bc..24310d7 100644 --- a/readme.md +++ b/readme.md @@ -43,6 +43,15 @@ It’s 100% JavaScript, fully customizable, and developer-first. +
+ +
+ OSS Capital +
+
+ Founded in 2018, OSS Capital is the first and only venture capital platform focused
exclusively on supporting early-stage COSS (commercial open source) startup founders.
+
+

From 8887f78ddc5a5755916ff53b7c4f9a2c845fa3df Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Sun, 20 Jun 2021 20:55:12 +0700 Subject: [PATCH 10/12] Add FAQ to the readme Closes #305 --- readme.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/readme.md b/readme.md index 24310d7..3af49af 100644 --- a/readme.md +++ b/readme.md @@ -620,6 +620,12 @@ queryString.stringify({foo: undefined}); //=> '' ``` +## FAQ + +### Why is it parsing `+` as a space? + +See [this answer](https://github.com/sindresorhus/query-string/issues/305). + ## query-string for enterprise Available as part of the Tidelift Subscription. From fd3e7792e0ec0fb72925627869a4d583ed832e54 Mon Sep 17 00:00:00 2001 From: Richie Bendall Date: Tue, 22 Jun 2021 05:30:44 +1200 Subject: [PATCH 11/12] Don't encode the fragment identifier in `.pick` and `.exclude` (#320) --- index.js | 10 +++++++--- test/exclude.js | 4 ++++ test/pick.js | 4 ++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 7ab5d92..cc57637 100644 --- a/index.js +++ b/index.js @@ -6,6 +6,8 @@ const filterObject = require('filter-obj'); const isNullOrUndefined = value => value === null || value === undefined; +const encodeFragmentIdentifier = Symbol('encodeFragmentIdentifier'); + function encoderForArrayFormat(options) { switch (options.arrayFormat) { case 'index': @@ -402,7 +404,8 @@ exports.parseUrl = (url, options) => { exports.stringifyUrl = (object, options) => { options = Object.assign({ encode: true, - strict: true + strict: true, + [encodeFragmentIdentifier]: true }, options); const url = removeHash(object.url).split('?')[0] || ''; @@ -417,7 +420,7 @@ exports.stringifyUrl = (object, options) => { let hash = getHash(object.url); if (object.fragmentIdentifier) { - hash = `#${encode(object.fragmentIdentifier, options)}`; + hash = `#${options[encodeFragmentIdentifier] ? encode(object.fragmentIdentifier, options) : object.fragmentIdentifier}`; } return `${url}${queryString}${hash}`; @@ -425,7 +428,8 @@ exports.stringifyUrl = (object, options) => { exports.pick = (input, filter, options) => { options = Object.assign({ - parseFragmentIdentifier: true + parseFragmentIdentifier: true, + [encodeFragmentIdentifier]: false }, options); const {url, query, fragmentIdentifier} = exports.parseUrl(input, options); diff --git a/test/exclude.js b/test/exclude.js index 91e0d4f..646db88 100644 --- a/test/exclude.js +++ b/test/exclude.js @@ -15,3 +15,7 @@ test('excludes elements in a URL with a filter predicate', t => { parseNumbers: true }), 'http://example.com/?b=2&c=3#a'); }); + +test('excludes elements in a URL without encoding fragment identifiers', t => { + t.is(queryString.exclude('https://example.com?a=b#/home', ['a']), 'https://example.com#/home'); +}); diff --git a/test/pick.js b/test/pick.js index e5e4381..0bfaf72 100644 --- a/test/pick.js +++ b/test/pick.js @@ -15,3 +15,7 @@ test('picks elements in a URL with a filter predicate', t => { parseNumbers: true }), 'http://example.com/?a=1#a'); }); + +test('picks elements in a URL without encoding fragment identifiers', t => { + t.is(queryString.pick('https://example.com?a=b#/home', []), 'https://example.com#/home'); +}); From b03e2e709664834e6cdcf50cd84b0056cdb5b7ff Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Tue, 22 Jun 2021 00:33:16 +0700 Subject: [PATCH 12/12] 7.0.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2680bc1..c1a4a75 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "query-string", - "version": "7.0.0", + "version": "7.0.1", "description": "Parse and stringify URL query strings", "license": "MIT", "repository": "sindresorhus/query-string",