diff options
Diffstat (limited to 'node_modules/query-string/index.js')
-rw-r--r-- | node_modules/query-string/index.js | 144 |
1 files changed, 122 insertions, 22 deletions
diff --git a/node_modules/query-string/index.js b/node_modules/query-string/index.js index 35c13e1a6..de1d7b182 100644 --- a/node_modules/query-string/index.js +++ b/node_modules/query-string/index.js @@ -3,12 +3,19 @@ const strictUriEncode = require('strict-uri-encode'); const decodeComponent = require('decode-uri-component'); const splitOnFirst = require('split-on-first'); +const isNullOrUndefined = value => value === null || value === undefined; + function encoderForArrayFormat(options) { switch (options.arrayFormat) { case 'index': return key => (result, value) => { const index = result.length; - if (value === undefined) { + + if ( + value === undefined || + (options.skipNull && value === null) || + (options.skipEmptyString && value === '') + ) { return result; } @@ -24,7 +31,11 @@ function encoderForArrayFormat(options) { case 'bracket': return key => (result, value) => { - if (value === undefined) { + if ( + value === undefined || + (options.skipNull && value === null) || + (options.skipEmptyString && value === '') + ) { return result; } @@ -36,21 +47,26 @@ function encoderForArrayFormat(options) { }; case 'comma': - return key => (result, value, index) => { + case 'separator': + return key => (result, value) => { if (value === null || value === undefined || value.length === 0) { return result; } - if (index === 0) { + if (result.length === 0) { return [[encode(key, options), '=', encode(value, options)].join('')]; } - return [[result, encode(value, options)].join(',')]; + return [[result, encode(value, options)].join(options.arrayFormatSeparator)]; }; default: return key => (result, value) => { - if (value === undefined) { + if ( + value === undefined || + (options.skipNull && value === null) || + (options.skipEmptyString && value === '') + ) { return result; } @@ -104,9 +120,10 @@ function parserForArrayFormat(options) { }; case 'comma': + case 'separator': return (key, value, accumulator) => { - const isArray = typeof value === 'string' && value.split('').indexOf(',') > -1; - const newValue = isArray ? value.split(',') : value; + const isArray = typeof value === 'string' && value.split('').indexOf(options.arrayFormatSeparator) > -1; + const newValue = isArray ? value.split(options.arrayFormatSeparator).map(item => decode(item, options)) : value === null ? value : decode(value, options); accumulator[key] = newValue; }; @@ -122,6 +139,12 @@ function parserForArrayFormat(options) { } } +function validateArrayFormatSeparator(value) { + if (typeof value !== 'string' || value.length !== 1) { + throw new TypeError('arrayFormatSeparator must be single character string'); + } +} + function encode(value, options) { if (options.encode) { return options.strict ? strictUriEncode(value) : encodeURIComponent(value); @@ -161,6 +184,16 @@ function removeHash(input) { return input; } +function getHash(url) { + let hash = ''; + const hashStart = url.indexOf('#'); + if (hashStart !== -1) { + hash = url.slice(hashStart); + } + + return hash; +} + function extract(input) { input = removeHash(input); const queryStart = input.indexOf('?'); @@ -171,15 +204,28 @@ 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, sort: true, arrayFormat: 'none', + arrayFormatSeparator: ',', parseNumbers: false, parseBooleans: false }, options); + validateArrayFormatSeparator(options.arrayFormatSeparator); + const formatter = parserForArrayFormat(options); // Create an object with no prototype @@ -196,19 +242,23 @@ function parse(input, options) { } for (const param of input.split('&')) { - let [key, value] = splitOnFirst(param.replace(/\+/g, ' '), '='); + let [key, value] = splitOnFirst(options.decode ? param.replace(/\+/g, ' ') : param, '='); // Missing `=` should be `null`: // http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters - value = value === undefined ? null : decode(value, options); + value = value === undefined ? null : ['comma', 'separator'].includes(options.arrayFormat) ? value : decode(value, options); + formatter(decode(key, options), value, ret); + } - 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'; + for (const key of Object.keys(ret)) { + const value = ret[key]; + if (typeof value === 'object' && value !== null) { + for (const k of Object.keys(value)) { + value[k] = parseValue(value[k], options); + } + } else { + ret[key] = parseValue(value, options); } - - formatter(decode(key, options), value, ret); } if (options.sort === false) { @@ -239,11 +289,28 @@ exports.stringify = (object, options) => { options = Object.assign({ encode: true, strict: true, - arrayFormat: 'none' + arrayFormat: 'none', + arrayFormatSeparator: ',' }, options); + validateArrayFormatSeparator(options.arrayFormatSeparator); + + const shouldFilter = key => ( + (options.skipNull && isNullOrUndefined(object[key])) || + (options.skipEmptyString && object[key] === '') + ); + const formatter = encoderForArrayFormat(options); - const keys = Object.keys(object); + + const objectCopy = {}; + + for (const key of Object.keys(object)) { + if (!shouldFilter(key)) { + objectCopy[key] = object[key]; + } + } + + const keys = Object.keys(objectCopy); if (options.sort !== false) { keys.sort(options.sort); @@ -271,8 +338,41 @@ exports.stringify = (object, options) => { }; exports.parseUrl = (input, options) => { - return { - url: removeHash(input).split('?')[0] || '', - query: parse(extract(input), options) - }; + options = Object.assign({ + decode: true + }, options); + + const [url, hash] = splitOnFirst(input, '#'); + + return Object.assign( + { + url: url.split('?')[0] || '', + query: parse(extract(input), options) + }, + options && options.parseFragmentIdentifier && hash ? {fragmentIdentifier: decode(hash, options)} : {} + ); +}; + +exports.stringifyUrl = (input, options) => { + options = Object.assign({ + encode: true, + strict: true + }, options); + + const url = removeHash(input.url).split('?')[0] || ''; + const queryFromUrl = exports.extract(input.url); + const parsedQueryFromUrl = exports.parse(queryFromUrl, {sort: false}); + + const query = Object.assign(parsedQueryFromUrl, input.query); + let queryString = exports.stringify(query, options); + if (queryString) { + queryString = `?${queryString}`; + } + + let hash = getHash(input.url); + if (input.fragmentIdentifier) { + hash = `#${encode(input.fragmentIdentifier, options)}`; + } + + return `${url}${queryString}${hash}`; }; |