Skip to content

Commit

Permalink
fix: allow parsing of empty keys in objects
Browse files Browse the repository at this point in the history
  • Loading branch information
Coobaha committed Mar 5, 2022
1 parent 542a5c7 commit d11a8ec
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 2 deletions.
9 changes: 7 additions & 2 deletions lib/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ var isArray = Array.isArray;

var defaults = {
allowDots: false,
allowEmptyKeys: false,
allowPrototypes: false,
allowSparse: false,
arrayLimit: 20,
Expand Down Expand Up @@ -83,6 +84,9 @@ var parseValues = function parseQueryStringValues(str, options) {
var key, val;
if (pos === -1) {
key = options.decoder(part, defaults.decoder, charset, 'key');
if (key === '') {
continue;
}
val = options.strictNullHandling ? null : '';
} else {
key = options.decoder(part.slice(0, pos), defaults.decoder, charset, 'key');
Expand Down Expand Up @@ -148,7 +152,7 @@ var parseObject = function (chain, val, options, valuesParsed) {
};

var parseKeys = function parseQueryStringKeys(givenKey, val, options, valuesParsed) {
if (!givenKey) {
if (options.allowEmptyKeys ? givenKey !== '' && !givenKey : !givenKey) {
return;
}

Expand All @@ -168,7 +172,7 @@ var parseKeys = function parseQueryStringKeys(givenKey, val, options, valuesPars
// Stash the parent if it exists

var keys = [];
if (parent) {
if (parent || (options.allowEmptyKeys && parent === '')) {
// If we aren't using plain objects, optionally prefix keys that would overwrite object prototype properties
if (!options.plainObjects && has.call(Object.prototype, parent)) {
if (!options.allowPrototypes) {
Expand Down Expand Up @@ -217,6 +221,7 @@ var normalizeParseOptions = function normalizeParseOptions(opts) {

return {
allowDots: typeof opts.allowDots === 'undefined' ? defaults.allowDots : !!opts.allowDots,
allowEmptyKeys: typeof opts.allowEmptyKeys === 'undefined' ? defaults.allowEmptyKeys : !!opts.allowEmptyKeys,
allowPrototypes: typeof opts.allowPrototypes === 'boolean' ? opts.allowPrototypes : defaults.allowPrototypes,
allowSparse: typeof opts.allowSparse === 'boolean' ? opts.allowSparse : defaults.allowSparse,
arrayLimit: typeof opts.arrayLimit === 'number' ? opts.arrayLimit : defaults.arrayLimit,
Expand Down
49 changes: 49 additions & 0 deletions test/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -843,3 +843,52 @@ test('parse()', function (t) {

t.end();
});

test('parse/stringify empty key WIP', function (t) {
t.test('parses an object with empty string key', function (st) {
st.deepEqual(qs.parse('=a', { allowEmptyKeys: true }), { '': 'a' });
st.deepEqual(qs.parse('=', { allowEmptyKeys: true }), { '': '' });

st.deepEqual(qs.parse('=&', { allowEmptyKeys: true }), { '': '' });
st.deepEqual(qs.parse('&=&', { allowEmptyKeys: true }), { '': '' });
st.deepEqual(qs.parse('&=', { allowEmptyKeys: true }), { '': '' });
st.deepEqual(qs.parse('&', { allowEmptyKeys: true }), { });
st.deepEqual(qs.parse('=&&&', { allowEmptyKeys: true }), { '': '' });

st.deepEqual(qs.parse('[deep]=a&[deep]=2', { allowEmptyKeys: true }), { '': { deep: ['a', '2'] } });
st.deepEqual(qs.parse('=a&=b', { allowEmptyKeys: true }), { '': ['a', 'b'] });
st.deepEqual(qs.parse('%5B0%5D=a&%5B1%5D=b', { allowEmptyKeys: true }), { '': ['a', 'b'] });
st.deepEqual(qs.parse('=a&foo=b', { allowEmptyKeys: true }), { '': 'a', foo: 'b' });
st.deepEqual(qs.parse('[]=a&[]=b& []=1', { allowEmptyKeys: true }), { '': ['a', 'b'], ' ': ['1'] });

st.deepEqual(qs.parse('=&a[]=b', { allowEmptyKeys: true }), { '': '', a: ['b'] });
st.deepEqual(qs.parse('=&a[]=b&a[1]=c', { allowEmptyKeys: true }), { '': '', a: ['b', 'c'] });
st.deepEqual(qs.parse('=&a[]=b&a[]=c&a[2]=d', { allowEmptyKeys: true }), { '': '', a: ['b', 'c', 'd'] });

st.deepEqual(qs.parse('a=b&a[]=c&=', { allowEmptyKeys: true }), { '': '', a: ['b', 'c'] });
st.deepEqual(qs.parse('a[]=b&a=c&=', { allowEmptyKeys: true }), { '': '', a: ['b', 'c'] });
st.deepEqual(qs.parse('a[0]=b&a=c&=', { allowEmptyKeys: true }), { '': '', a: ['b', 'c'] });
st.deepEqual(qs.parse('a=b&a[0]=c&=', { allowEmptyKeys: true }), { '': '', a: ['b', 'c'] });

st.deepEqual(qs.parse('a[]=b&a=c&=', { allowEmptyKeys: true }), { '': '', a: ['b', 'c'] });

st.end();

});

t.test('stringifies an object with empty string key', function (st) {
st.deepEqual(qs.stringify({ '': 'a' }), '=a');
st.deepEqual(qs.stringify({ '': ['a', 'b'] }), '%5B0%5D=a&%5B1%5D=b');
st.deepEqual(qs.stringify({ '': ['a', 'b'], a: [1, 2] }, { encode: false }), '[0]=a&[1]=b&a[0]=1&a[1]=2');

st.end();
});
t.test('edge case with object/arrays', function (st) {

st.deepEqual(qs.stringify({ '': { '': [2, 3] } }, { encode: false }), '[][0]=2&[][1]=3');

st.deepEqual(qs.parse('[][0]=2&[][1]=3', { allowEmptyKeys: true }), { '': { '': [2, 3] } });

st.end();
});
});

0 comments on commit d11a8ec

Please sign in to comment.