blob: 2d22df2a7c4a5ffb09c5b646a43f6723359eedf4 [file] [log] [blame]
'use strict';
var strictUriEncode = require('strict-uri-encode');
var objectAssign = require('object-assign');
function encoderForArrayFormat(opts) {
switch (opts.arrayFormat) {
case 'index':
return function (key, value, index) {
return value === null ? [
encode(key, opts),
'[',
index,
']'
].join('') : [
encode(key, opts),
'[',
encode(index, opts),
']=',
encode(value, opts)
].join('');
};
case 'bracket':
return function (key, value) {
return value === null ? encode(key, opts) : [
encode(key, opts),
'[]=',
encode(value, opts)
].join('');
};
default:
return function (key, value) {
return value === null ? encode(key, opts) : [
encode(key, opts),
'=',
encode(value, opts)
].join('');
};
}
}
function parserForArrayFormat(opts) {
var result;
switch (opts.arrayFormat) {
case 'index':
return function (key, value, accumulator) {
result = /\[(\d*)\]$/.exec(key);
key = key.replace(/\[\d*\]$/, '');
if (!result) {
accumulator[key] = value;
return;
}
if (accumulator[key] === undefined) {
accumulator[key] = {};
}
accumulator[key][result[1]] = value;
};
case 'bracket':
return function (key, value, accumulator) {
result = /(\[\])$/.exec(key);
key = key.replace(/\[\]$/, '');
if (!result) {
accumulator[key] = value;
return;
} else if (accumulator[key] === undefined) {
accumulator[key] = [value];
return;
}
accumulator[key] = [].concat(accumulator[key], value);
};
default:
return function (key, value, accumulator) {
if (accumulator[key] === undefined) {
accumulator[key] = value;
return;
}
accumulator[key] = [].concat(accumulator[key], value);
};
}
}
function encode(value, opts) {
if (opts.encode) {
return opts.strict ? strictUriEncode(value) : encodeURIComponent(value);
}
return value;
}
function keysSorter(input) {
if (Array.isArray(input)) {
return input.sort();
} else if (typeof input === 'object') {
return keysSorter(Object.keys(input)).sort(function (a, b) {
return Number(a) - Number(b);
}).map(function (key) {
return input[key];
});
}
return input;
}
exports.extract = function (str) {
return str.split('?')[1] || '';
};
exports.parse = function (str, opts) {
opts = objectAssign({arrayFormat: 'none'}, opts);
var formatter = parserForArrayFormat(opts);
// Create an object with no prototype
// https://github.com/sindresorhus/query-string/issues/47
var ret = Object.create(null);
if (typeof str !== 'string') {
return ret;
}
str = str.trim().replace(/^(\?|#|&)/, '');
if (!str) {
return ret;
}
str.split('&').forEach(function (param) {
var parts = param.replace(/\+/g, ' ').split('=');
// Firefox (pre 40) decodes `%3D` to `=`
// https://github.com/sindresorhus/query-string/pull/37
var key = parts.shift();
var val = parts.length > 0 ? parts.join('=') : undefined;
// missing `=` should be `null`:
// http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters
val = val === undefined ? null : decodeURIComponent(val);
formatter(decodeURIComponent(key), val, ret);
});
return Object.keys(ret).sort().reduce(function (result, key) {
var val = ret[key];
if (Boolean(val) && typeof val === 'object' && !Array.isArray(val)) {
// Sort object keys, not values
result[key] = keysSorter(val);
} else {
result[key] = val;
}
return result;
}, Object.create(null));
};
exports.stringify = function (obj, opts) {
var defaults = {
encode: true,
strict: true,
arrayFormat: 'none'
};
opts = objectAssign(defaults, opts);
var formatter = encoderForArrayFormat(opts);
return obj ? Object.keys(obj).sort().map(function (key) {
var val = obj[key];
if (val === undefined) {
return '';
}
if (val === null) {
return encode(key, opts);
}
if (Array.isArray(val)) {
var result = [];
val.slice().forEach(function (val2) {
if (val2 === undefined) {
return;
}
result.push(formatter(key, val2, result.length));
});
return result.join('&');
}
return encode(key, opts) + '=' + encode(val, opts);
}).filter(function (x) {
return x.length > 0;
}).join('&') : '';
};