From 02079f02ba3ca361f615bb72bd0fe483c7df56ed Mon Sep 17 00:00:00 2001 From: Yao Ding Date: Thu, 8 Aug 2019 10:36:26 -0400 Subject: [PATCH 1/3] make parseNumbers and parseBooleans work with arrayFormat --- index.js | 28 +++++++++++++++++++++------- test/parse.js | 21 +++++++++++++++++++++ 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index 35c13e1a..c8246d05 100644 --- a/index.js +++ b/index.js @@ -171,6 +171,16 @@ function extract(input) { return input.slice(queryStart + 1); } +function parseValue(value, options) { + if (options.parseNumbers && !Number.isNaN(Number(value)) && (typeof value === 'string' && value.trim() !== '')) { + value = Number(value); + } else if (options.parseBooleans && value !== null && (value.toLowerCase() === 'true' || value.toLowerCase() === 'false')) { + value = value.toLowerCase() === 'true'; + } + + return value; +} + function parse(input, options) { options = Object.assign({ decode: true, @@ -201,16 +211,20 @@ function parse(input, options) { // Missing `=` should be `null`: // http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters value = value === undefined ? null : decode(value, options); - - if (options.parseNumbers && !Number.isNaN(Number(value)) && (typeof value === 'string' && value.trim() !== '')) { - value = Number(value); - } else if (options.parseBooleans && value !== null && (value.toLowerCase() === 'true' || value.toLowerCase() === 'false')) { - value = value.toLowerCase() === 'true'; - } - formatter(decode(key, options), value, ret); } + Object.keys(ret).forEach(key => { + const value = ret[key]; + if (Boolean(value) && typeof value === 'object') { + Object.keys(value).forEach(k => { + value[k] = parseValue(value[k], options); + }); + } else { + ret[key] = parseValue(value, options); + } + }); + if (options.sort === false) { return ret; } diff --git a/test/parse.js b/test/parse.js index 8273b71b..5f714e3a 100644 --- a/test/parse.js +++ b/test/parse.js @@ -246,6 +246,13 @@ test('NaN value returns as string if option is set', t => { t.deepEqual(queryString.parse('foo= &bar=', {parseNumbers: true}), {foo: ' ', bar: ''}); }); +test('parseNumbers works with arrayFormat', t => { + t.deepEqual(queryString.parse('foo[]=1&foo[]=2&foo[]=3&bar=1', {parseNumbers: true, arrayFormat: 'bracket'}), {foo: [1, 2, 3], bar: 1}); + t.deepEqual(queryString.parse('foo=1,2,a', {parseNumbers: true, arrayFormat: 'comma'}), {foo: [1, 2, 'a']}); + t.deepEqual(queryString.parse('foo[0]=1&foo[1]=2&foo[2]', {parseNumbers: true, arrayFormat: 'index'}), {foo: [1, 2, null]}); + t.deepEqual(queryString.parse('foo=1&foo=2&foo=3', {parseNumbers: true}), {foo: [1, 2, 3]}); +}); + test('boolean value returns as string by default', t => { t.deepEqual(queryString.parse('foo=true'), {foo: 'true'}); }); @@ -255,7 +262,21 @@ test('boolean value returns as boolean if option is set', t => { t.deepEqual(queryString.parse('foo=false&bar=true', {parseBooleans: true}), {foo: false, bar: true}); }); +test('parseBooleans works with arrayFormat', t => { + t.deepEqual(queryString.parse('foo[]=true&foo[]=false&foo[]=true&bar=1', {parseBooleans: true, arrayFormat: 'bracket'}), {foo: [true, false, true], bar: '1'}); + t.deepEqual(queryString.parse('foo=true,false,a', {parseBooleans: true, arrayFormat: 'comma'}), {foo: [true, false, 'a']}); + t.deepEqual(queryString.parse('foo[0]=true&foo[1]=false&foo[2]', {parseBooleans: true, arrayFormat: 'index'}), {foo: [true, false, null]}); + t.deepEqual(queryString.parse('foo=true&foo=false&foo=3', {parseBooleans: true}), {foo: [true, false, '3']}); +}); + test('boolean value returns as boolean and number value as number if both options are set', t => { t.deepEqual(queryString.parse('foo=true&bar=1.12', {parseNumbers: true, parseBooleans: true}), {foo: true, bar: 1.12}); t.deepEqual(queryString.parse('foo=16.32&bar=false', {parseNumbers: true, parseBooleans: true}), {foo: 16.32, bar: false}); }); + +test('parseNumbers and parseBooleans can work with arrayFormat at the same time', t => { + t.deepEqual(queryString.parse('foo=true&foo=false&bar=1.12&bar=2', {parseNumbers: true, parseBooleans: true}), {foo: [true, false], bar: [1.12, 2]}); + t.deepEqual(queryString.parse('foo[]=true&foo[]=false&foo[]=true&bar[]=1&bar[]=2', {parseNumbers: true, parseBooleans: true, arrayFormat: 'bracket'}), {foo: [true, false, true], bar: [1, 2]}); + t.deepEqual(queryString.parse('foo=true,false&bar=1,2', {parseNumbers: true, parseBooleans: true, arrayFormat: 'comma'}), {foo: [true, false], bar: [1, 2]}); + t.deepEqual(queryString.parse('foo[0]=true&foo[1]=false&bar[0]=1&bar[1]=2', {parseNumbers: true, parseBooleans: true, arrayFormat: 'index'}), {foo: [true, false], bar: [1, 2]}); +}); From ab2a856f32b6309747c9d3b1337e128ac2df0d48 Mon Sep 17 00:00:00 2001 From: Yao Ding Date: Thu, 8 Aug 2019 23:00:21 -0400 Subject: [PATCH 2/3] Update index.js Co-Authored-By: Sindre Sorhus --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index c8246d05..bde020d9 100644 --- a/index.js +++ b/index.js @@ -216,7 +216,7 @@ function parse(input, options) { Object.keys(ret).forEach(key => { const value = ret[key]; - if (Boolean(value) && typeof value === 'object') { + if (typeof value === 'object' && value !== null) { Object.keys(value).forEach(k => { value[k] = parseValue(value[k], options); }); From dfe902f4cbcf9ca564068bee14939d3af88aaa5a Mon Sep 17 00:00:00 2001 From: Yao Ding Date: Thu, 8 Aug 2019 23:03:52 -0400 Subject: [PATCH 3/3] replace forEach with for of --- index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index bde020d9..b122f275 100644 --- a/index.js +++ b/index.js @@ -214,16 +214,16 @@ function parse(input, options) { formatter(decode(key, options), value, ret); } - Object.keys(ret).forEach(key => { + for (const key of Object.keys(ret)) { const value = ret[key]; if (typeof value === 'object' && value !== null) { - Object.keys(value).forEach(k => { + for (const k of Object.keys(value)) { value[k] = parseValue(value[k], options); - }); + } } else { ret[key] = parseValue(value, options); } - }); + } if (options.sort === false) { return ret;