webpack-config-utils
Advanced tools
Comparing version 2.3.0 to 2.3.1
@@ -16,6 +16,6 @@ 'use strict'; | ||
* ... your config | ||
* entry: removeEmptyProperties({ | ||
* app: ifProd('./indexWithoutCSS', './indexWithCSS'), | ||
* css: ifNotProd('./style.css') | ||
* }), | ||
* entry: removeEmpty({ | ||
* app: ifProduction('./indexWithoutCSS', './indexWithCSS'), | ||
* css: ifNotProduction('./style.css') | ||
* }), | ||
* plugins: removeEmpty([ | ||
@@ -22,0 +22,0 @@ * ifProduction(new webpack.optimize.DedupePlugin()), |
@@ -0,1 +1,123 @@ | ||
## **6.5.2** | ||
- [Fix] use `safer-buffer` instead of `Buffer` constructor | ||
- [Refactor] utils: `module.exports` one thing, instead of mutating `exports` (#230) | ||
- [Dev Deps] update `browserify`, `eslint`, `iconv-lite`, `safer-buffer`, `tape`, `browserify` | ||
## **6.5.1** | ||
- [Fix] Fix parsing & compacting very deep objects (#224) | ||
- [Refactor] name utils functions | ||
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `tape` | ||
- [Tests] up to `node` `v8.4`; use `nvm install-latest-npm` so newer npm doesnβt break older node | ||
- [Tests] Use precise dist for Node.js 0.6 runtime (#225) | ||
- [Tests] make 0.6 required, now that itβs passing | ||
- [Tests] on `node` `v8.2`; fix npm on node 0.6 | ||
## **6.5.0** | ||
- [New] add `utils.assign` | ||
- [New] pass default encoder/decoder to custom encoder/decoder functions (#206) | ||
- [New] `parse`/`stringify`: add `ignoreQueryPrefix`/`addQueryPrefix` options, respectively (#213) | ||
- [Fix] Handle stringifying empty objects with addQueryPrefix (#217) | ||
- [Fix] do not mutate `options` argument (#207) | ||
- [Refactor] `parse`: cache index to reuse in else statement (#182) | ||
- [Docs] add various badges to readme (#208) | ||
- [Dev Deps] update `eslint`, `browserify`, `iconv-lite`, `tape` | ||
- [Tests] up to `node` `v8.1`, `v7.10`, `v6.11`; npm v4.6 breaks on node < v1; npm v5+ breaks on node < v4 | ||
- [Tests] add `editorconfig-tools` | ||
## **6.4.0** | ||
- [New] `qs.stringify`: add `encodeValuesOnly` option | ||
- [Fix] follow `allowPrototypes` option during merge (#201, #201) | ||
- [Fix] support keys starting with brackets (#202, #200) | ||
- [Fix] chmod a-x | ||
- [Dev Deps] update `eslint` | ||
- [Tests] up to `node` `v7.7`, `v6.10`,` v4.8`; disable osx builds since they block linux builds | ||
- [eslint] reduce warnings | ||
## **6.3.2** | ||
- [Fix] follow `allowPrototypes` option during merge (#201, #200) | ||
- [Dev Deps] update `eslint` | ||
- [Fix] chmod a-x | ||
- [Fix] support keys starting with brackets (#202, #200) | ||
- [Tests] up to `node` `v7.7`, `v6.10`,` v4.8`; disable osx builds since they block linux builds | ||
## **6.3.1** | ||
- [Fix] ensure that `allowPrototypes: false` does not ever shadow Object.prototype properties (thanks, @snyk!) | ||
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `browserify`, `iconv-lite`, `qs-iconv`, `tape` | ||
- [Tests] on all node minors; improve test matrix | ||
- [Docs] document stringify option `allowDots` (#195) | ||
- [Docs] add empty object and array values example (#195) | ||
- [Docs] Fix minor inconsistency/typo (#192) | ||
- [Docs] document stringify option `sort` (#191) | ||
- [Refactor] `stringify`: throw faster with an invalid encoder | ||
- [Refactor] remove unnecessary escapes (#184) | ||
- Remove contributing.md, since `qs` is no longer part of `hapi` (#183) | ||
## **6.3.0** | ||
- [New] Add support for RFC 1738 (#174, #173) | ||
- [New] `stringify`: Add `serializeDate` option to customize Date serialization (#159) | ||
- [Fix] ensure `utils.merge` handles merging two arrays | ||
- [Refactor] only constructors should be capitalized | ||
- [Refactor] capitalized var names are for constructors only | ||
- [Refactor] avoid using a sparse array | ||
- [Robustness] `formats`: cache `String#replace` | ||
- [Dev Deps] update `browserify`, `eslint`, `@ljharb/eslint-config`; add `safe-publish-latest` | ||
- [Tests] up to `node` `v6.8`, `v4.6`; improve test matrix | ||
- [Tests] flesh out arrayLimit/arrayFormat tests (#107) | ||
- [Tests] skip Object.create tests when null objects are not available | ||
- [Tests] Turn on eslint for test files (#175) | ||
## **6.2.3** | ||
- [Fix] follow `allowPrototypes` option during merge (#201, #200) | ||
- [Fix] chmod a-x | ||
- [Fix] support keys starting with brackets (#202, #200) | ||
- [Tests] up to `node` `v7.7`, `v6.10`,` v4.8`; disable osx builds since they block linux builds | ||
## **6.2.2** | ||
- [Fix] ensure that `allowPrototypes: false` does not ever shadow Object.prototype properties | ||
## **6.2.1** | ||
- [Fix] ensure `key[]=x&key[]&key[]=y` results in 3, not 2, values | ||
- [Refactor] Be explicit and use `Object.prototype.hasOwnProperty.call` | ||
- [Tests] remove `parallelshell` since it does not reliably report failures | ||
- [Tests] up to `node` `v6.3`, `v5.12` | ||
- [Dev Deps] update `tape`, `eslint`, `@ljharb/eslint-config`, `qs-iconv` | ||
## [**6.2.0**](https://github.com/ljharb/qs/issues?milestone=36&state=closed) | ||
- [New] pass Buffers to the encoder/decoder directly (#161) | ||
- [New] add "encoder" and "decoder" options, for custom param encoding/decoding (#160) | ||
- [Fix] fix compacting of nested sparse arrays (#150) | ||
## **6.1.2 | ||
- [Fix] follow `allowPrototypes` option during merge (#201, #200) | ||
- [Fix] chmod a-x | ||
- [Fix] support keys starting with brackets (#202, #200) | ||
- [Tests] up to `node` `v7.7`, `v6.10`,` v4.8`; disable osx builds since they block linux builds | ||
## **6.1.1** | ||
- [Fix] ensure that `allowPrototypes: false` does not ever shadow Object.prototype properties | ||
## [**6.1.0**](https://github.com/ljharb/qs/issues?milestone=35&state=closed) | ||
- [New] allowDots option for `stringify` (#151) | ||
- [Fix] "sort" option should work at a depth of 3 or more (#151) | ||
- [Fix] Restore `dist` directory; will be removed in v7 (#148) | ||
## **6.0.4** | ||
- [Fix] follow `allowPrototypes` option during merge (#201, #200) | ||
- [Fix] chmod a-x | ||
- [Fix] support keys starting with brackets (#202, #200) | ||
- [Tests] up to `node` `v7.7`, `v6.10`,` v4.8`; disable osx builds since they block linux builds | ||
## **6.0.3** | ||
- [Fix] ensure that `allowPrototypes: false` does not ever shadow Object.prototype properties | ||
- [Fix] Restore `dist` directory; will be removed in v7 (#148) | ||
## [**6.0.2**](https://github.com/ljharb/qs/issues?milestone=33&state=closed) | ||
- Revert ES6 requirement and restore support for node down to v0.8. | ||
## [**6.0.1**](https://github.com/ljharb/qs/issues?milestone=32&state=closed) | ||
- [**#127**](https://github.com/ljharb/qs/pull/127) Fix engines definition in package.json | ||
## [**6.0.0**](https://github.com/ljharb/qs/issues?milestone=31&state=closed) | ||
- [**#124**](https://github.com/ljharb/qs/issues/124) Use ES6 and drop support for node < v4 | ||
## **5.2.1** | ||
@@ -2,0 +124,0 @@ - [Fix] ensure `key[]=x&key[]&key[]=y` results in 3, not 2, values |
@@ -1,56 +0,74 @@ | ||
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Qs = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ | ||
// Load modules | ||
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Qs = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ | ||
'use strict'; | ||
var Stringify = require('./stringify'); | ||
var Parse = require('./parse'); | ||
var replace = String.prototype.replace; | ||
var percentTwenties = /%20/g; | ||
module.exports = { | ||
'default': 'RFC3986', | ||
formatters: { | ||
RFC1738: function (value) { | ||
return replace.call(value, percentTwenties, '+'); | ||
}, | ||
RFC3986: function (value) { | ||
return value; | ||
} | ||
}, | ||
RFC1738: 'RFC1738', | ||
RFC3986: 'RFC3986' | ||
}; | ||
// Declare internals | ||
},{}],2:[function(require,module,exports){ | ||
'use strict'; | ||
var internals = {}; | ||
var stringify = require('./stringify'); | ||
var parse = require('./parse'); | ||
var formats = require('./formats'); | ||
module.exports = { | ||
stringify: Stringify, | ||
parse: Parse | ||
formats: formats, | ||
parse: parse, | ||
stringify: stringify | ||
}; | ||
},{"./parse":2,"./stringify":3}],2:[function(require,module,exports){ | ||
// Load modules | ||
},{"./formats":1,"./parse":3,"./stringify":4}],3:[function(require,module,exports){ | ||
'use strict'; | ||
var Utils = require('./utils'); | ||
var utils = require('./utils'); | ||
var has = Object.prototype.hasOwnProperty; | ||
// Declare internals | ||
var internals = { | ||
var defaults = { | ||
allowDots: false, | ||
allowPrototypes: false, | ||
arrayLimit: 20, | ||
decoder: utils.decode, | ||
delimiter: '&', | ||
depth: 5, | ||
arrayLimit: 20, | ||
parameterLimit: 1000, | ||
strictNullHandling: false, | ||
plainObjects: false, | ||
allowPrototypes: false, | ||
allowDots: false | ||
strictNullHandling: false | ||
}; | ||
internals.parseValues = function (str, options) { | ||
var parseValues = function parseQueryStringValues(str, options) { | ||
var obj = {}; | ||
var parts = str.split(options.delimiter, options.parameterLimit === Infinity ? undefined : options.parameterLimit); | ||
var cleanStr = options.ignoreQueryPrefix ? str.replace(/^\?/, '') : str; | ||
var limit = options.parameterLimit === Infinity ? undefined : options.parameterLimit; | ||
var parts = cleanStr.split(options.delimiter, limit); | ||
for (var i = 0, il = parts.length; i < il; ++i) { | ||
for (var i = 0; i < parts.length; ++i) { | ||
var part = parts[i]; | ||
var pos = part.indexOf(']=') === -1 ? part.indexOf('=') : part.indexOf(']=') + 1; | ||
var bracketEqualsPos = part.indexOf(']='); | ||
var pos = bracketEqualsPos === -1 ? part.indexOf('=') : bracketEqualsPos + 1; | ||
var key, val; | ||
if (pos === -1) { | ||
key = Utils.decode(part); | ||
key = options.decoder(part, defaults.decoder); | ||
val = options.strictNullHandling ? null : ''; | ||
} else { | ||
key = Utils.decode(part.slice(0, pos)); | ||
val = Utils.decode(part.slice(pos + 1)); | ||
key = options.decoder(part.slice(0, pos), defaults.decoder); | ||
val = options.decoder(part.slice(pos + 1), defaults.decoder); | ||
} | ||
if (Object.prototype.hasOwnProperty.call(obj, key)) { | ||
if (has.call(obj, key)) { | ||
obj[key] = [].concat(obj[key]).concat(val); | ||
@@ -65,43 +83,38 @@ } else { | ||
var parseObject = function (chain, val, options) { | ||
var leaf = val; | ||
internals.parseObject = function (chain, val, options) { | ||
for (var i = chain.length - 1; i >= 0; --i) { | ||
var obj; | ||
var root = chain[i]; | ||
if (!chain.length) { | ||
return val; | ||
} | ||
var root = chain.shift(); | ||
var obj; | ||
if (root === '[]') { | ||
obj = []; | ||
obj = obj.concat(internals.parseObject(chain, val, options)); | ||
} | ||
else { | ||
obj = options.plainObjects ? Object.create(null) : {}; | ||
var cleanRoot = root[0] === '[' && root[root.length - 1] === ']' ? root.slice(1, root.length - 1) : root; | ||
var index = parseInt(cleanRoot, 10); | ||
var indexString = '' + index; | ||
if (!isNaN(index) && | ||
root !== cleanRoot && | ||
indexString === cleanRoot && | ||
index >= 0 && | ||
(options.parseArrays && | ||
index <= options.arrayLimit)) { | ||
if (root === '[]') { | ||
obj = []; | ||
obj[index] = internals.parseObject(chain, val, options); | ||
obj = obj.concat(leaf); | ||
} else { | ||
obj = options.plainObjects ? Object.create(null) : {}; | ||
var cleanRoot = root.charAt(0) === '[' && root.charAt(root.length - 1) === ']' ? root.slice(1, -1) : root; | ||
var index = parseInt(cleanRoot, 10); | ||
if ( | ||
!isNaN(index) | ||
&& root !== cleanRoot | ||
&& String(index) === cleanRoot | ||
&& index >= 0 | ||
&& (options.parseArrays && index <= options.arrayLimit) | ||
) { | ||
obj = []; | ||
obj[index] = leaf; | ||
} else { | ||
obj[cleanRoot] = leaf; | ||
} | ||
} | ||
else { | ||
obj[cleanRoot] = internals.parseObject(chain, val, options); | ||
} | ||
leaf = obj; | ||
} | ||
return obj; | ||
return leaf; | ||
}; | ||
internals.parseKeys = function (key, val, options) { | ||
if (!key) { | ||
var parseKeys = function parseQueryStringKeys(givenKey, val, options) { | ||
if (!givenKey) { | ||
return; | ||
@@ -111,15 +124,13 @@ } | ||
// Transform dot notation to bracket notation | ||
var key = options.allowDots ? givenKey.replace(/\.([^.[]+)/g, '[$1]') : givenKey; | ||
if (options.allowDots) { | ||
key = key.replace(/\.([^\.\[]+)/g, '[$1]'); | ||
} | ||
// The regex chunks | ||
var parent = /^([^\[\]]*)/; | ||
var child = /(\[[^\[\]]*\])/g; | ||
var brackets = /(\[[^[\]]*])/; | ||
var child = /(\[[^[\]]*])/g; | ||
// Get the parent | ||
var segment = parent.exec(key); | ||
var segment = brackets.exec(key); | ||
var parent = segment ? key.slice(0, segment.index) : key; | ||
@@ -129,8 +140,6 @@ // Stash the parent if it exists | ||
var keys = []; | ||
if (segment[1]) { | ||
if (parent) { | ||
// If we aren't using plain objects, optionally prefix keys | ||
// that would overwrite object prototype properties | ||
if (!options.plainObjects && | ||
Object.prototype.hasOwnProperty(segment[1])) { | ||
if (!options.plainObjects && has.call(Object.prototype, parent)) { | ||
if (!options.allowPrototypes) { | ||
@@ -141,3 +150,3 @@ return; | ||
keys.push(segment[1]); | ||
keys.push(parent); | ||
} | ||
@@ -149,9 +158,6 @@ | ||
while ((segment = child.exec(key)) !== null && i < options.depth) { | ||
++i; | ||
if (!options.plainObjects && | ||
Object.prototype.hasOwnProperty(segment[1].replace(/\[|\]/g, ''))) { | ||
i += 1; | ||
if (!options.plainObjects && has.call(Object.prototype, segment[1].slice(1, -1))) { | ||
if (!options.allowPrototypes) { | ||
continue; | ||
return; | ||
} | ||
@@ -168,27 +174,29 @@ } | ||
return internals.parseObject(keys, val, options); | ||
return parseObject(keys, val, options); | ||
}; | ||
module.exports = function (str, opts) { | ||
var options = opts ? utils.assign({}, opts) : {}; | ||
module.exports = function (str, options) { | ||
if (options.decoder !== null && options.decoder !== undefined && typeof options.decoder !== 'function') { | ||
throw new TypeError('Decoder has to be a function.'); | ||
} | ||
options = options || {}; | ||
options.delimiter = typeof options.delimiter === 'string' || Utils.isRegExp(options.delimiter) ? options.delimiter : internals.delimiter; | ||
options.depth = typeof options.depth === 'number' ? options.depth : internals.depth; | ||
options.arrayLimit = typeof options.arrayLimit === 'number' ? options.arrayLimit : internals.arrayLimit; | ||
options.ignoreQueryPrefix = options.ignoreQueryPrefix === true; | ||
options.delimiter = typeof options.delimiter === 'string' || utils.isRegExp(options.delimiter) ? options.delimiter : defaults.delimiter; | ||
options.depth = typeof options.depth === 'number' ? options.depth : defaults.depth; | ||
options.arrayLimit = typeof options.arrayLimit === 'number' ? options.arrayLimit : defaults.arrayLimit; | ||
options.parseArrays = options.parseArrays !== false; | ||
options.allowDots = typeof options.allowDots === 'boolean' ? options.allowDots : internals.allowDots; | ||
options.plainObjects = typeof options.plainObjects === 'boolean' ? options.plainObjects : internals.plainObjects; | ||
options.allowPrototypes = typeof options.allowPrototypes === 'boolean' ? options.allowPrototypes : internals.allowPrototypes; | ||
options.parameterLimit = typeof options.parameterLimit === 'number' ? options.parameterLimit : internals.parameterLimit; | ||
options.strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : internals.strictNullHandling; | ||
options.decoder = typeof options.decoder === 'function' ? options.decoder : defaults.decoder; | ||
options.allowDots = typeof options.allowDots === 'boolean' ? options.allowDots : defaults.allowDots; | ||
options.plainObjects = typeof options.plainObjects === 'boolean' ? options.plainObjects : defaults.plainObjects; | ||
options.allowPrototypes = typeof options.allowPrototypes === 'boolean' ? options.allowPrototypes : defaults.allowPrototypes; | ||
options.parameterLimit = typeof options.parameterLimit === 'number' ? options.parameterLimit : defaults.parameterLimit; | ||
options.strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : defaults.strictNullHandling; | ||
if (str === '' || | ||
str === null || | ||
typeof str === 'undefined') { | ||
if (str === '' || str === null || typeof str === 'undefined') { | ||
return options.plainObjects ? Object.create(null) : {}; | ||
} | ||
var tempObj = typeof str === 'string' ? internals.parseValues(str, options) : str; | ||
var tempObj = typeof str === 'string' ? parseValues(str, options) : str; | ||
var obj = options.plainObjects ? Object.create(null) : {}; | ||
@@ -199,55 +207,65 @@ | ||
var keys = Object.keys(tempObj); | ||
for (var i = 0, il = keys.length; i < il; ++i) { | ||
for (var i = 0; i < keys.length; ++i) { | ||
var key = keys[i]; | ||
var newObj = internals.parseKeys(key, tempObj[key], options); | ||
obj = Utils.merge(obj, newObj, options); | ||
var newObj = parseKeys(key, tempObj[key], options); | ||
obj = utils.merge(obj, newObj, options); | ||
} | ||
return Utils.compact(obj); | ||
return utils.compact(obj); | ||
}; | ||
},{"./utils":4}],3:[function(require,module,exports){ | ||
// Load modules | ||
},{"./utils":5}],4:[function(require,module,exports){ | ||
'use strict'; | ||
var Utils = require('./utils'); | ||
var utils = require('./utils'); | ||
var formats = require('./formats'); | ||
var arrayPrefixGenerators = { | ||
brackets: function brackets(prefix) { // eslint-disable-line func-name-matching | ||
return prefix + '[]'; | ||
}, | ||
indices: function indices(prefix, key) { // eslint-disable-line func-name-matching | ||
return prefix + '[' + key + ']'; | ||
}, | ||
repeat: function repeat(prefix) { // eslint-disable-line func-name-matching | ||
return prefix; | ||
} | ||
}; | ||
// Declare internals | ||
var toISO = Date.prototype.toISOString; | ||
var internals = { | ||
var defaults = { | ||
delimiter: '&', | ||
arrayPrefixGenerators: { | ||
brackets: function (prefix, key) { | ||
return prefix + '[]'; | ||
}, | ||
indices: function (prefix, key) { | ||
return prefix + '[' + key + ']'; | ||
}, | ||
repeat: function (prefix, key) { | ||
return prefix; | ||
} | ||
encode: true, | ||
encoder: utils.encode, | ||
encodeValuesOnly: false, | ||
serializeDate: function serializeDate(date) { // eslint-disable-line func-name-matching | ||
return toISO.call(date); | ||
}, | ||
strictNullHandling: false, | ||
skipNulls: false, | ||
encode: true | ||
strictNullHandling: false | ||
}; | ||
internals.stringify = function (obj, prefix, generateArrayPrefix, strictNullHandling, skipNulls, encode, filter, sort) { | ||
var stringify = function stringify( // eslint-disable-line func-name-matching | ||
object, | ||
prefix, | ||
generateArrayPrefix, | ||
strictNullHandling, | ||
skipNulls, | ||
encoder, | ||
filter, | ||
sort, | ||
allowDots, | ||
serializeDate, | ||
formatter, | ||
encodeValuesOnly | ||
) { | ||
var obj = object; | ||
if (typeof filter === 'function') { | ||
obj = filter(prefix, obj); | ||
} | ||
else if (Utils.isBuffer(obj)) { | ||
obj = obj.toString(); | ||
} | ||
else if (obj instanceof Date) { | ||
obj = obj.toISOString(); | ||
} | ||
else if (obj === null) { | ||
} else if (obj instanceof Date) { | ||
obj = serializeDate(obj); | ||
} else if (obj === null) { | ||
if (strictNullHandling) { | ||
return encode ? Utils.encode(prefix) : prefix; | ||
return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder) : prefix; | ||
} | ||
@@ -258,10 +276,8 @@ | ||
if (typeof obj === 'string' || | ||
typeof obj === 'number' || | ||
typeof obj === 'boolean') { | ||
if (encode) { | ||
return [Utils.encode(prefix) + '=' + Utils.encode(obj)]; | ||
if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean' || utils.isBuffer(obj)) { | ||
if (encoder) { | ||
var keyValue = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder); | ||
return [formatter(keyValue) + '=' + formatter(encoder(obj, defaults.encoder))]; | ||
} | ||
return [prefix + '=' + obj]; | ||
return [formatter(prefix) + '=' + formatter(String(obj))]; | ||
} | ||
@@ -283,8 +299,6 @@ | ||
for (var i = 0, il = objKeys.length; i < il; ++i) { | ||
for (var i = 0; i < objKeys.length; ++i) { | ||
var key = objKeys[i]; | ||
if (skipNulls && | ||
obj[key] === null) { | ||
if (skipNulls && obj[key] === null) { | ||
continue; | ||
@@ -294,7 +308,32 @@ } | ||
if (Array.isArray(obj)) { | ||
values = values.concat(internals.stringify(obj[key], generateArrayPrefix(prefix, key), generateArrayPrefix, strictNullHandling, skipNulls, encode, filter)); | ||
values = values.concat(stringify( | ||
obj[key], | ||
generateArrayPrefix(prefix, key), | ||
generateArrayPrefix, | ||
strictNullHandling, | ||
skipNulls, | ||
encoder, | ||
filter, | ||
sort, | ||
allowDots, | ||
serializeDate, | ||
formatter, | ||
encodeValuesOnly | ||
)); | ||
} else { | ||
values = values.concat(stringify( | ||
obj[key], | ||
prefix + (allowDots ? '.' + key : '[' + key + ']'), | ||
generateArrayPrefix, | ||
strictNullHandling, | ||
skipNulls, | ||
encoder, | ||
filter, | ||
sort, | ||
allowDots, | ||
serializeDate, | ||
formatter, | ||
encodeValuesOnly | ||
)); | ||
} | ||
else { | ||
values = values.concat(internals.stringify(obj[key], prefix + '[' + key + ']', generateArrayPrefix, strictNullHandling, skipNulls, encode, filter)); | ||
} | ||
} | ||
@@ -305,26 +344,39 @@ | ||
module.exports = function (object, opts) { | ||
var obj = object; | ||
var options = opts ? utils.assign({}, opts) : {}; | ||
module.exports = function (obj, options) { | ||
if (options.encoder !== null && options.encoder !== undefined && typeof options.encoder !== 'function') { | ||
throw new TypeError('Encoder has to be a function.'); | ||
} | ||
options = options || {}; | ||
var delimiter = typeof options.delimiter === 'undefined' ? internals.delimiter : options.delimiter; | ||
var strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : internals.strictNullHandling; | ||
var skipNulls = typeof options.skipNulls === 'boolean' ? options.skipNulls : internals.skipNulls; | ||
var encode = typeof options.encode === 'boolean' ? options.encode : internals.encode; | ||
var delimiter = typeof options.delimiter === 'undefined' ? defaults.delimiter : options.delimiter; | ||
var strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : defaults.strictNullHandling; | ||
var skipNulls = typeof options.skipNulls === 'boolean' ? options.skipNulls : defaults.skipNulls; | ||
var encode = typeof options.encode === 'boolean' ? options.encode : defaults.encode; | ||
var encoder = typeof options.encoder === 'function' ? options.encoder : defaults.encoder; | ||
var sort = typeof options.sort === 'function' ? options.sort : null; | ||
var allowDots = typeof options.allowDots === 'undefined' ? false : options.allowDots; | ||
var serializeDate = typeof options.serializeDate === 'function' ? options.serializeDate : defaults.serializeDate; | ||
var encodeValuesOnly = typeof options.encodeValuesOnly === 'boolean' ? options.encodeValuesOnly : defaults.encodeValuesOnly; | ||
if (typeof options.format === 'undefined') { | ||
options.format = formats['default']; | ||
} else if (!Object.prototype.hasOwnProperty.call(formats.formatters, options.format)) { | ||
throw new TypeError('Unknown format option provided.'); | ||
} | ||
var formatter = formats.formatters[options.format]; | ||
var objKeys; | ||
var filter; | ||
if (typeof options.filter === 'function') { | ||
filter = options.filter; | ||
obj = filter('', obj); | ||
} else if (Array.isArray(options.filter)) { | ||
filter = options.filter; | ||
objKeys = filter; | ||
} | ||
else if (Array.isArray(options.filter)) { | ||
objKeys = filter = options.filter; | ||
} | ||
var keys = []; | ||
if (typeof obj !== 'object' || | ||
obj === null) { | ||
if (typeof obj !== 'object' || obj === null) { | ||
return ''; | ||
@@ -334,13 +386,11 @@ } | ||
var arrayFormat; | ||
if (options.arrayFormat in internals.arrayPrefixGenerators) { | ||
if (options.arrayFormat in arrayPrefixGenerators) { | ||
arrayFormat = options.arrayFormat; | ||
} | ||
else if ('indices' in options) { | ||
} else if ('indices' in options) { | ||
arrayFormat = options.indices ? 'indices' : 'repeat'; | ||
} | ||
else { | ||
} else { | ||
arrayFormat = 'indices'; | ||
} | ||
var generateArrayPrefix = internals.arrayPrefixGenerators[arrayFormat]; | ||
var generateArrayPrefix = arrayPrefixGenerators[arrayFormat]; | ||
@@ -355,37 +405,62 @@ if (!objKeys) { | ||
for (var i = 0, il = objKeys.length; i < il; ++i) { | ||
for (var i = 0; i < objKeys.length; ++i) { | ||
var key = objKeys[i]; | ||
if (skipNulls && | ||
obj[key] === null) { | ||
if (skipNulls && obj[key] === null) { | ||
continue; | ||
} | ||
keys = keys.concat(internals.stringify(obj[key], key, generateArrayPrefix, strictNullHandling, skipNulls, encode, filter, sort)); | ||
keys = keys.concat(stringify( | ||
obj[key], | ||
key, | ||
generateArrayPrefix, | ||
strictNullHandling, | ||
skipNulls, | ||
encode ? encoder : null, | ||
filter, | ||
sort, | ||
allowDots, | ||
serializeDate, | ||
formatter, | ||
encodeValuesOnly | ||
)); | ||
} | ||
return keys.join(delimiter); | ||
var joined = keys.join(delimiter); | ||
var prefix = options.addQueryPrefix === true ? '?' : ''; | ||
return joined.length > 0 ? prefix + joined : ''; | ||
}; | ||
},{"./utils":4}],4:[function(require,module,exports){ | ||
// Load modules | ||
},{"./formats":1,"./utils":5}],5:[function(require,module,exports){ | ||
'use strict'; | ||
var has = Object.prototype.hasOwnProperty; | ||
// Declare internals | ||
var hexTable = (function () { | ||
var array = []; | ||
for (var i = 0; i < 256; ++i) { | ||
array.push('%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase()); | ||
} | ||
var internals = {}; | ||
internals.hexTable = new Array(256); | ||
for (var h = 0; h < 256; ++h) { | ||
internals.hexTable[h] = '%' + ((h < 16 ? '0' : '') + h.toString(16)).toUpperCase(); | ||
} | ||
return array; | ||
}()); | ||
var compactQueue = function compactQueue(queue) { | ||
var obj; | ||
exports.arrayToObject = function (source, options) { | ||
while (queue.length) { | ||
var item = queue.pop(); | ||
obj = item.obj[item.prop]; | ||
var obj = options.plainObjects ? Object.create(null) : {}; | ||
for (var i = 0, il = source.length; i < il; ++i) { | ||
if (typeof source[i] !== 'undefined') { | ||
if (Array.isArray(obj)) { | ||
var compacted = []; | ||
obj[i] = source[i]; | ||
for (var j = 0; j < obj.length; ++j) { | ||
if (typeof obj[j] !== 'undefined') { | ||
compacted.push(obj[j]); | ||
} | ||
} | ||
item.obj[item.prop] = compacted; | ||
} | ||
@@ -397,5 +472,14 @@ } | ||
var arrayToObject = function arrayToObject(source, options) { | ||
var obj = options && options.plainObjects ? Object.create(null) : {}; | ||
for (var i = 0; i < source.length; ++i) { | ||
if (typeof source[i] !== 'undefined') { | ||
obj[i] = source[i]; | ||
} | ||
} | ||
exports.merge = function (target, source, options) { | ||
return obj; | ||
}; | ||
var merge = function merge(target, source, options) { | ||
if (!source) { | ||
@@ -408,9 +492,9 @@ return target; | ||
target.push(source); | ||
} else if (typeof target === 'object') { | ||
if (options.plainObjects || options.allowPrototypes || !has.call(Object.prototype, source)) { | ||
target[source] = true; | ||
} | ||
} else { | ||
return [target, source]; | ||
} | ||
else if (typeof target === 'object') { | ||
target[source] = true; | ||
} | ||
else { | ||
target = [target, source]; | ||
} | ||
@@ -421,31 +505,45 @@ return target; | ||
if (typeof target !== 'object') { | ||
target = [target].concat(source); | ||
return target; | ||
return [target].concat(source); | ||
} | ||
if (Array.isArray(target) && | ||
!Array.isArray(source)) { | ||
var mergeTarget = target; | ||
if (Array.isArray(target) && !Array.isArray(source)) { | ||
mergeTarget = arrayToObject(target, options); | ||
} | ||
target = exports.arrayToObject(target, options); | ||
if (Array.isArray(target) && Array.isArray(source)) { | ||
source.forEach(function (item, i) { | ||
if (has.call(target, i)) { | ||
if (target[i] && typeof target[i] === 'object') { | ||
target[i] = merge(target[i], item, options); | ||
} else { | ||
target.push(item); | ||
} | ||
} else { | ||
target[i] = item; | ||
} | ||
}); | ||
return target; | ||
} | ||
var keys = Object.keys(source); | ||
for (var k = 0, kl = keys.length; k < kl; ++k) { | ||
var key = keys[k]; | ||
return Object.keys(source).reduce(function (acc, key) { | ||
var value = source[key]; | ||
if (!Object.prototype.hasOwnProperty.call(target, key)) { | ||
target[key] = value; | ||
if (has.call(acc, key)) { | ||
acc[key] = merge(acc[key], value, options); | ||
} else { | ||
acc[key] = value; | ||
} | ||
else { | ||
target[key] = exports.merge(target[key], value, options); | ||
} | ||
} | ||
return acc; | ||
}, mergeTarget); | ||
}; | ||
return target; | ||
var assign = function assignSingleSource(target, source) { | ||
return Object.keys(source).reduce(function (acc, key) { | ||
acc[key] = source[key]; | ||
return acc; | ||
}, target); | ||
}; | ||
exports.decode = function (str) { | ||
var decode = function (str) { | ||
try { | ||
@@ -458,4 +556,3 @@ return decodeURIComponent(str.replace(/\+/g, ' ')); | ||
exports.encode = function (str) { | ||
var encode = function encode(str) { | ||
// This code was originally written by Brian White (mscdex) for the io.js core querystring library. | ||
@@ -467,19 +564,18 @@ // It has been adapted here for stricter adherence to RFC 3986 | ||
if (typeof str !== 'string') { | ||
str = '' + str; | ||
} | ||
var string = typeof str === 'string' ? str : String(str); | ||
var out = ''; | ||
for (var i = 0, il = str.length; i < il; ++i) { | ||
var c = str.charCodeAt(i); | ||
for (var i = 0; i < string.length; ++i) { | ||
var c = string.charCodeAt(i); | ||
if (c === 0x2D || // - | ||
c === 0x2E || // . | ||
c === 0x5F || // _ | ||
c === 0x7E || // ~ | ||
(c >= 0x30 && c <= 0x39) || // 0-9 | ||
(c >= 0x41 && c <= 0x5A) || // a-z | ||
(c >= 0x61 && c <= 0x7A)) { // A-Z | ||
out += str[i]; | ||
if ( | ||
c === 0x2D // - | ||
|| c === 0x2E // . | ||
|| c === 0x5F // _ | ||
|| c === 0x7E // ~ | ||
|| (c >= 0x30 && c <= 0x39) // 0-9 | ||
|| (c >= 0x41 && c <= 0x5A) // a-z | ||
|| (c >= 0x61 && c <= 0x7A) // A-Z | ||
) { | ||
out += string.charAt(i); | ||
continue; | ||
@@ -489,3 +585,3 @@ } | ||
if (c < 0x80) { | ||
out += internals.hexTable[c]; | ||
out = out + hexTable[c]; | ||
continue; | ||
@@ -495,3 +591,3 @@ } | ||
if (c < 0x800) { | ||
out += internals.hexTable[0xC0 | (c >> 6)] + internals.hexTable[0x80 | (c & 0x3F)]; | ||
out = out + (hexTable[0xC0 | (c >> 6)] + hexTable[0x80 | (c & 0x3F)]); | ||
continue; | ||
@@ -501,9 +597,12 @@ } | ||
if (c < 0xD800 || c >= 0xE000) { | ||
out += internals.hexTable[0xE0 | (c >> 12)] + internals.hexTable[0x80 | ((c >> 6) & 0x3F)] + internals.hexTable[0x80 | (c & 0x3F)]; | ||
out = out + (hexTable[0xE0 | (c >> 12)] + hexTable[0x80 | ((c >> 6) & 0x3F)] + hexTable[0x80 | (c & 0x3F)]); | ||
continue; | ||
} | ||
++i; | ||
c = 0x10000 + (((c & 0x3FF) << 10) | (str.charCodeAt(i) & 0x3FF)); | ||
out += internals.hexTable[0xF0 | (c >> 18)] + internals.hexTable[0x80 | ((c >> 12) & 0x3F)] + internals.hexTable[0x80 | ((c >> 6) & 0x3F)] + internals.hexTable[0x80 | (c & 0x3F)]; | ||
i += 1; | ||
c = 0x10000 + (((c & 0x3FF) << 10) | (string.charCodeAt(i) & 0x3FF)); | ||
out += hexTable[0xF0 | (c >> 18)] | ||
+ hexTable[0x80 | ((c >> 12) & 0x3F)] | ||
+ hexTable[0x80 | ((c >> 6) & 0x3F)] | ||
+ hexTable[0x80 | (c & 0x3F)]; | ||
} | ||
@@ -514,60 +613,48 @@ | ||
exports.compact = function (obj, refs) { | ||
var compact = function compact(value) { | ||
var queue = [{ obj: { o: value }, prop: 'o' }]; | ||
var refs = []; | ||
if (typeof obj !== 'object' || | ||
obj === null) { | ||
for (var i = 0; i < queue.length; ++i) { | ||
var item = queue[i]; | ||
var obj = item.obj[item.prop]; | ||
return obj; | ||
} | ||
refs = refs || []; | ||
var lookup = refs.indexOf(obj); | ||
if (lookup !== -1) { | ||
return refs[lookup]; | ||
} | ||
refs.push(obj); | ||
if (Array.isArray(obj)) { | ||
var compacted = []; | ||
for (var i = 0, il = obj.length; i < il; ++i) { | ||
if (typeof obj[i] !== 'undefined') { | ||
compacted.push(obj[i]); | ||
var keys = Object.keys(obj); | ||
for (var j = 0; j < keys.length; ++j) { | ||
var key = keys[j]; | ||
var val = obj[key]; | ||
if (typeof val === 'object' && val !== null && refs.indexOf(val) === -1) { | ||
queue.push({ obj: obj, prop: key }); | ||
refs.push(val); | ||
} | ||
} | ||
return compacted; | ||
} | ||
var keys = Object.keys(obj); | ||
for (i = 0, il = keys.length; i < il; ++i) { | ||
var key = keys[i]; | ||
obj[key] = exports.compact(obj[key], refs); | ||
} | ||
return obj; | ||
return compactQueue(queue); | ||
}; | ||
exports.isRegExp = function (obj) { | ||
var isRegExp = function isRegExp(obj) { | ||
return Object.prototype.toString.call(obj) === '[object RegExp]'; | ||
}; | ||
exports.isBuffer = function (obj) { | ||
if (obj === null || | ||
typeof obj === 'undefined') { | ||
var isBuffer = function isBuffer(obj) { | ||
if (obj === null || typeof obj === 'undefined') { | ||
return false; | ||
} | ||
return !!(obj.constructor && | ||
obj.constructor.isBuffer && | ||
obj.constructor.isBuffer(obj)); | ||
return !!(obj.constructor && obj.constructor.isBuffer && obj.constructor.isBuffer(obj)); | ||
}; | ||
},{}]},{},[1])(1) | ||
}); | ||
module.exports = { | ||
arrayToObject: arrayToObject, | ||
assign: assign, | ||
compact: compact, | ||
decode: decode, | ||
encode: encode, | ||
isBuffer: isBuffer, | ||
isRegExp: isRegExp, | ||
merge: merge | ||
}; | ||
},{}]},{},[2])(2) | ||
}); |
@@ -1,15 +0,11 @@ | ||
// Load modules | ||
'use strict'; | ||
var Stringify = require('./stringify'); | ||
var Parse = require('./parse'); | ||
var stringify = require('./stringify'); | ||
var parse = require('./parse'); | ||
var formats = require('./formats'); | ||
// Declare internals | ||
var internals = {}; | ||
module.exports = { | ||
stringify: Stringify, | ||
parse: Parse | ||
formats: formats, | ||
parse: parse, | ||
stringify: stringify | ||
}; |
@@ -1,38 +0,40 @@ | ||
// Load modules | ||
'use strict'; | ||
var Utils = require('./utils'); | ||
var utils = require('./utils'); | ||
var has = Object.prototype.hasOwnProperty; | ||
// Declare internals | ||
var internals = { | ||
var defaults = { | ||
allowDots: false, | ||
allowPrototypes: false, | ||
arrayLimit: 20, | ||
decoder: utils.decode, | ||
delimiter: '&', | ||
depth: 5, | ||
arrayLimit: 20, | ||
parameterLimit: 1000, | ||
strictNullHandling: false, | ||
plainObjects: false, | ||
allowPrototypes: false, | ||
allowDots: false | ||
strictNullHandling: false | ||
}; | ||
internals.parseValues = function (str, options) { | ||
var parseValues = function parseQueryStringValues(str, options) { | ||
var obj = {}; | ||
var parts = str.split(options.delimiter, options.parameterLimit === Infinity ? undefined : options.parameterLimit); | ||
var cleanStr = options.ignoreQueryPrefix ? str.replace(/^\?/, '') : str; | ||
var limit = options.parameterLimit === Infinity ? undefined : options.parameterLimit; | ||
var parts = cleanStr.split(options.delimiter, limit); | ||
for (var i = 0, il = parts.length; i < il; ++i) { | ||
for (var i = 0; i < parts.length; ++i) { | ||
var part = parts[i]; | ||
var pos = part.indexOf(']=') === -1 ? part.indexOf('=') : part.indexOf(']=') + 1; | ||
var bracketEqualsPos = part.indexOf(']='); | ||
var pos = bracketEqualsPos === -1 ? part.indexOf('=') : bracketEqualsPos + 1; | ||
var key, val; | ||
if (pos === -1) { | ||
key = Utils.decode(part); | ||
key = options.decoder(part, defaults.decoder); | ||
val = options.strictNullHandling ? null : ''; | ||
} else { | ||
key = Utils.decode(part.slice(0, pos)); | ||
val = Utils.decode(part.slice(pos + 1)); | ||
key = options.decoder(part.slice(0, pos), defaults.decoder); | ||
val = options.decoder(part.slice(pos + 1), defaults.decoder); | ||
} | ||
if (Object.prototype.hasOwnProperty.call(obj, key)) { | ||
if (has.call(obj, key)) { | ||
obj[key] = [].concat(obj[key]).concat(val); | ||
@@ -47,43 +49,38 @@ } else { | ||
var parseObject = function (chain, val, options) { | ||
var leaf = val; | ||
internals.parseObject = function (chain, val, options) { | ||
for (var i = chain.length - 1; i >= 0; --i) { | ||
var obj; | ||
var root = chain[i]; | ||
if (!chain.length) { | ||
return val; | ||
} | ||
var root = chain.shift(); | ||
var obj; | ||
if (root === '[]') { | ||
obj = []; | ||
obj = obj.concat(internals.parseObject(chain, val, options)); | ||
} | ||
else { | ||
obj = options.plainObjects ? Object.create(null) : {}; | ||
var cleanRoot = root[0] === '[' && root[root.length - 1] === ']' ? root.slice(1, root.length - 1) : root; | ||
var index = parseInt(cleanRoot, 10); | ||
var indexString = '' + index; | ||
if (!isNaN(index) && | ||
root !== cleanRoot && | ||
indexString === cleanRoot && | ||
index >= 0 && | ||
(options.parseArrays && | ||
index <= options.arrayLimit)) { | ||
if (root === '[]') { | ||
obj = []; | ||
obj[index] = internals.parseObject(chain, val, options); | ||
obj = obj.concat(leaf); | ||
} else { | ||
obj = options.plainObjects ? Object.create(null) : {}; | ||
var cleanRoot = root.charAt(0) === '[' && root.charAt(root.length - 1) === ']' ? root.slice(1, -1) : root; | ||
var index = parseInt(cleanRoot, 10); | ||
if ( | ||
!isNaN(index) | ||
&& root !== cleanRoot | ||
&& String(index) === cleanRoot | ||
&& index >= 0 | ||
&& (options.parseArrays && index <= options.arrayLimit) | ||
) { | ||
obj = []; | ||
obj[index] = leaf; | ||
} else { | ||
obj[cleanRoot] = leaf; | ||
} | ||
} | ||
else { | ||
obj[cleanRoot] = internals.parseObject(chain, val, options); | ||
} | ||
leaf = obj; | ||
} | ||
return obj; | ||
return leaf; | ||
}; | ||
internals.parseKeys = function (key, val, options) { | ||
if (!key) { | ||
var parseKeys = function parseQueryStringKeys(givenKey, val, options) { | ||
if (!givenKey) { | ||
return; | ||
@@ -93,15 +90,13 @@ } | ||
// Transform dot notation to bracket notation | ||
var key = options.allowDots ? givenKey.replace(/\.([^.[]+)/g, '[$1]') : givenKey; | ||
if (options.allowDots) { | ||
key = key.replace(/\.([^\.\[]+)/g, '[$1]'); | ||
} | ||
// The regex chunks | ||
var parent = /^([^\[\]]*)/; | ||
var child = /(\[[^\[\]]*\])/g; | ||
var brackets = /(\[[^[\]]*])/; | ||
var child = /(\[[^[\]]*])/g; | ||
// Get the parent | ||
var segment = parent.exec(key); | ||
var segment = brackets.exec(key); | ||
var parent = segment ? key.slice(0, segment.index) : key; | ||
@@ -111,8 +106,6 @@ // Stash the parent if it exists | ||
var keys = []; | ||
if (segment[1]) { | ||
if (parent) { | ||
// If we aren't using plain objects, optionally prefix keys | ||
// that would overwrite object prototype properties | ||
if (!options.plainObjects && | ||
Object.prototype.hasOwnProperty(segment[1])) { | ||
if (!options.plainObjects && has.call(Object.prototype, parent)) { | ||
if (!options.allowPrototypes) { | ||
@@ -123,3 +116,3 @@ return; | ||
keys.push(segment[1]); | ||
keys.push(parent); | ||
} | ||
@@ -131,9 +124,6 @@ | ||
while ((segment = child.exec(key)) !== null && i < options.depth) { | ||
++i; | ||
if (!options.plainObjects && | ||
Object.prototype.hasOwnProperty(segment[1].replace(/\[|\]/g, ''))) { | ||
i += 1; | ||
if (!options.plainObjects && has.call(Object.prototype, segment[1].slice(1, -1))) { | ||
if (!options.allowPrototypes) { | ||
continue; | ||
return; | ||
} | ||
@@ -150,27 +140,29 @@ } | ||
return internals.parseObject(keys, val, options); | ||
return parseObject(keys, val, options); | ||
}; | ||
module.exports = function (str, opts) { | ||
var options = opts ? utils.assign({}, opts) : {}; | ||
module.exports = function (str, options) { | ||
if (options.decoder !== null && options.decoder !== undefined && typeof options.decoder !== 'function') { | ||
throw new TypeError('Decoder has to be a function.'); | ||
} | ||
options = options || {}; | ||
options.delimiter = typeof options.delimiter === 'string' || Utils.isRegExp(options.delimiter) ? options.delimiter : internals.delimiter; | ||
options.depth = typeof options.depth === 'number' ? options.depth : internals.depth; | ||
options.arrayLimit = typeof options.arrayLimit === 'number' ? options.arrayLimit : internals.arrayLimit; | ||
options.ignoreQueryPrefix = options.ignoreQueryPrefix === true; | ||
options.delimiter = typeof options.delimiter === 'string' || utils.isRegExp(options.delimiter) ? options.delimiter : defaults.delimiter; | ||
options.depth = typeof options.depth === 'number' ? options.depth : defaults.depth; | ||
options.arrayLimit = typeof options.arrayLimit === 'number' ? options.arrayLimit : defaults.arrayLimit; | ||
options.parseArrays = options.parseArrays !== false; | ||
options.allowDots = typeof options.allowDots === 'boolean' ? options.allowDots : internals.allowDots; | ||
options.plainObjects = typeof options.plainObjects === 'boolean' ? options.plainObjects : internals.plainObjects; | ||
options.allowPrototypes = typeof options.allowPrototypes === 'boolean' ? options.allowPrototypes : internals.allowPrototypes; | ||
options.parameterLimit = typeof options.parameterLimit === 'number' ? options.parameterLimit : internals.parameterLimit; | ||
options.strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : internals.strictNullHandling; | ||
options.decoder = typeof options.decoder === 'function' ? options.decoder : defaults.decoder; | ||
options.allowDots = typeof options.allowDots === 'boolean' ? options.allowDots : defaults.allowDots; | ||
options.plainObjects = typeof options.plainObjects === 'boolean' ? options.plainObjects : defaults.plainObjects; | ||
options.allowPrototypes = typeof options.allowPrototypes === 'boolean' ? options.allowPrototypes : defaults.allowPrototypes; | ||
options.parameterLimit = typeof options.parameterLimit === 'number' ? options.parameterLimit : defaults.parameterLimit; | ||
options.strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : defaults.strictNullHandling; | ||
if (str === '' || | ||
str === null || | ||
typeof str === 'undefined') { | ||
if (str === '' || str === null || typeof str === 'undefined') { | ||
return options.plainObjects ? Object.create(null) : {}; | ||
} | ||
var tempObj = typeof str === 'string' ? internals.parseValues(str, options) : str; | ||
var tempObj = typeof str === 'string' ? parseValues(str, options) : str; | ||
var obj = options.plainObjects ? Object.create(null) : {}; | ||
@@ -181,9 +173,9 @@ | ||
var keys = Object.keys(tempObj); | ||
for (var i = 0, il = keys.length; i < il; ++i) { | ||
for (var i = 0; i < keys.length; ++i) { | ||
var key = keys[i]; | ||
var newObj = internals.parseKeys(key, tempObj[key], options); | ||
obj = Utils.merge(obj, newObj, options); | ||
var newObj = parseKeys(key, tempObj[key], options); | ||
obj = utils.merge(obj, newObj, options); | ||
} | ||
return Utils.compact(obj); | ||
return utils.compact(obj); | ||
}; |
@@ -1,44 +0,54 @@ | ||
// Load modules | ||
'use strict'; | ||
var Utils = require('./utils'); | ||
var utils = require('./utils'); | ||
var formats = require('./formats'); | ||
var arrayPrefixGenerators = { | ||
brackets: function brackets(prefix) { // eslint-disable-line func-name-matching | ||
return prefix + '[]'; | ||
}, | ||
indices: function indices(prefix, key) { // eslint-disable-line func-name-matching | ||
return prefix + '[' + key + ']'; | ||
}, | ||
repeat: function repeat(prefix) { // eslint-disable-line func-name-matching | ||
return prefix; | ||
} | ||
}; | ||
// Declare internals | ||
var toISO = Date.prototype.toISOString; | ||
var internals = { | ||
var defaults = { | ||
delimiter: '&', | ||
arrayPrefixGenerators: { | ||
brackets: function (prefix, key) { | ||
return prefix + '[]'; | ||
}, | ||
indices: function (prefix, key) { | ||
return prefix + '[' + key + ']'; | ||
}, | ||
repeat: function (prefix, key) { | ||
return prefix; | ||
} | ||
encode: true, | ||
encoder: utils.encode, | ||
encodeValuesOnly: false, | ||
serializeDate: function serializeDate(date) { // eslint-disable-line func-name-matching | ||
return toISO.call(date); | ||
}, | ||
strictNullHandling: false, | ||
skipNulls: false, | ||
encode: true | ||
strictNullHandling: false | ||
}; | ||
internals.stringify = function (obj, prefix, generateArrayPrefix, strictNullHandling, skipNulls, encode, filter, sort) { | ||
var stringify = function stringify( // eslint-disable-line func-name-matching | ||
object, | ||
prefix, | ||
generateArrayPrefix, | ||
strictNullHandling, | ||
skipNulls, | ||
encoder, | ||
filter, | ||
sort, | ||
allowDots, | ||
serializeDate, | ||
formatter, | ||
encodeValuesOnly | ||
) { | ||
var obj = object; | ||
if (typeof filter === 'function') { | ||
obj = filter(prefix, obj); | ||
} | ||
else if (Utils.isBuffer(obj)) { | ||
obj = obj.toString(); | ||
} | ||
else if (obj instanceof Date) { | ||
obj = obj.toISOString(); | ||
} | ||
else if (obj === null) { | ||
} else if (obj instanceof Date) { | ||
obj = serializeDate(obj); | ||
} else if (obj === null) { | ||
if (strictNullHandling) { | ||
return encode ? Utils.encode(prefix) : prefix; | ||
return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder) : prefix; | ||
} | ||
@@ -49,10 +59,8 @@ | ||
if (typeof obj === 'string' || | ||
typeof obj === 'number' || | ||
typeof obj === 'boolean') { | ||
if (encode) { | ||
return [Utils.encode(prefix) + '=' + Utils.encode(obj)]; | ||
if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean' || utils.isBuffer(obj)) { | ||
if (encoder) { | ||
var keyValue = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder); | ||
return [formatter(keyValue) + '=' + formatter(encoder(obj, defaults.encoder))]; | ||
} | ||
return [prefix + '=' + obj]; | ||
return [formatter(prefix) + '=' + formatter(String(obj))]; | ||
} | ||
@@ -74,8 +82,6 @@ | ||
for (var i = 0, il = objKeys.length; i < il; ++i) { | ||
for (var i = 0; i < objKeys.length; ++i) { | ||
var key = objKeys[i]; | ||
if (skipNulls && | ||
obj[key] === null) { | ||
if (skipNulls && obj[key] === null) { | ||
continue; | ||
@@ -85,7 +91,32 @@ } | ||
if (Array.isArray(obj)) { | ||
values = values.concat(internals.stringify(obj[key], generateArrayPrefix(prefix, key), generateArrayPrefix, strictNullHandling, skipNulls, encode, filter)); | ||
values = values.concat(stringify( | ||
obj[key], | ||
generateArrayPrefix(prefix, key), | ||
generateArrayPrefix, | ||
strictNullHandling, | ||
skipNulls, | ||
encoder, | ||
filter, | ||
sort, | ||
allowDots, | ||
serializeDate, | ||
formatter, | ||
encodeValuesOnly | ||
)); | ||
} else { | ||
values = values.concat(stringify( | ||
obj[key], | ||
prefix + (allowDots ? '.' + key : '[' + key + ']'), | ||
generateArrayPrefix, | ||
strictNullHandling, | ||
skipNulls, | ||
encoder, | ||
filter, | ||
sort, | ||
allowDots, | ||
serializeDate, | ||
formatter, | ||
encodeValuesOnly | ||
)); | ||
} | ||
else { | ||
values = values.concat(internals.stringify(obj[key], prefix + '[' + key + ']', generateArrayPrefix, strictNullHandling, skipNulls, encode, filter)); | ||
} | ||
} | ||
@@ -96,26 +127,39 @@ | ||
module.exports = function (object, opts) { | ||
var obj = object; | ||
var options = opts ? utils.assign({}, opts) : {}; | ||
module.exports = function (obj, options) { | ||
if (options.encoder !== null && options.encoder !== undefined && typeof options.encoder !== 'function') { | ||
throw new TypeError('Encoder has to be a function.'); | ||
} | ||
options = options || {}; | ||
var delimiter = typeof options.delimiter === 'undefined' ? internals.delimiter : options.delimiter; | ||
var strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : internals.strictNullHandling; | ||
var skipNulls = typeof options.skipNulls === 'boolean' ? options.skipNulls : internals.skipNulls; | ||
var encode = typeof options.encode === 'boolean' ? options.encode : internals.encode; | ||
var delimiter = typeof options.delimiter === 'undefined' ? defaults.delimiter : options.delimiter; | ||
var strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : defaults.strictNullHandling; | ||
var skipNulls = typeof options.skipNulls === 'boolean' ? options.skipNulls : defaults.skipNulls; | ||
var encode = typeof options.encode === 'boolean' ? options.encode : defaults.encode; | ||
var encoder = typeof options.encoder === 'function' ? options.encoder : defaults.encoder; | ||
var sort = typeof options.sort === 'function' ? options.sort : null; | ||
var allowDots = typeof options.allowDots === 'undefined' ? false : options.allowDots; | ||
var serializeDate = typeof options.serializeDate === 'function' ? options.serializeDate : defaults.serializeDate; | ||
var encodeValuesOnly = typeof options.encodeValuesOnly === 'boolean' ? options.encodeValuesOnly : defaults.encodeValuesOnly; | ||
if (typeof options.format === 'undefined') { | ||
options.format = formats['default']; | ||
} else if (!Object.prototype.hasOwnProperty.call(formats.formatters, options.format)) { | ||
throw new TypeError('Unknown format option provided.'); | ||
} | ||
var formatter = formats.formatters[options.format]; | ||
var objKeys; | ||
var filter; | ||
if (typeof options.filter === 'function') { | ||
filter = options.filter; | ||
obj = filter('', obj); | ||
} else if (Array.isArray(options.filter)) { | ||
filter = options.filter; | ||
objKeys = filter; | ||
} | ||
else if (Array.isArray(options.filter)) { | ||
objKeys = filter = options.filter; | ||
} | ||
var keys = []; | ||
if (typeof obj !== 'object' || | ||
obj === null) { | ||
if (typeof obj !== 'object' || obj === null) { | ||
return ''; | ||
@@ -125,13 +169,11 @@ } | ||
var arrayFormat; | ||
if (options.arrayFormat in internals.arrayPrefixGenerators) { | ||
if (options.arrayFormat in arrayPrefixGenerators) { | ||
arrayFormat = options.arrayFormat; | ||
} | ||
else if ('indices' in options) { | ||
} else if ('indices' in options) { | ||
arrayFormat = options.indices ? 'indices' : 'repeat'; | ||
} | ||
else { | ||
} else { | ||
arrayFormat = 'indices'; | ||
} | ||
var generateArrayPrefix = internals.arrayPrefixGenerators[arrayFormat]; | ||
var generateArrayPrefix = arrayPrefixGenerators[arrayFormat]; | ||
@@ -146,15 +188,29 @@ if (!objKeys) { | ||
for (var i = 0, il = objKeys.length; i < il; ++i) { | ||
for (var i = 0; i < objKeys.length; ++i) { | ||
var key = objKeys[i]; | ||
if (skipNulls && | ||
obj[key] === null) { | ||
if (skipNulls && obj[key] === null) { | ||
continue; | ||
} | ||
keys = keys.concat(internals.stringify(obj[key], key, generateArrayPrefix, strictNullHandling, skipNulls, encode, filter, sort)); | ||
keys = keys.concat(stringify( | ||
obj[key], | ||
key, | ||
generateArrayPrefix, | ||
strictNullHandling, | ||
skipNulls, | ||
encode ? encoder : null, | ||
filter, | ||
sort, | ||
allowDots, | ||
serializeDate, | ||
formatter, | ||
encodeValuesOnly | ||
)); | ||
} | ||
return keys.join(delimiter); | ||
var joined = keys.join(delimiter); | ||
var prefix = options.addQueryPrefix === true ? '?' : ''; | ||
return joined.length > 0 ? prefix + joined : ''; | ||
}; |
@@ -1,20 +0,31 @@ | ||
// Load modules | ||
'use strict'; | ||
var has = Object.prototype.hasOwnProperty; | ||
// Declare internals | ||
var hexTable = (function () { | ||
var array = []; | ||
for (var i = 0; i < 256; ++i) { | ||
array.push('%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase()); | ||
} | ||
var internals = {}; | ||
internals.hexTable = new Array(256); | ||
for (var h = 0; h < 256; ++h) { | ||
internals.hexTable[h] = '%' + ((h < 16 ? '0' : '') + h.toString(16)).toUpperCase(); | ||
} | ||
return array; | ||
}()); | ||
var compactQueue = function compactQueue(queue) { | ||
var obj; | ||
exports.arrayToObject = function (source, options) { | ||
while (queue.length) { | ||
var item = queue.pop(); | ||
obj = item.obj[item.prop]; | ||
var obj = options.plainObjects ? Object.create(null) : {}; | ||
for (var i = 0, il = source.length; i < il; ++i) { | ||
if (typeof source[i] !== 'undefined') { | ||
if (Array.isArray(obj)) { | ||
var compacted = []; | ||
obj[i] = source[i]; | ||
for (var j = 0; j < obj.length; ++j) { | ||
if (typeof obj[j] !== 'undefined') { | ||
compacted.push(obj[j]); | ||
} | ||
} | ||
item.obj[item.prop] = compacted; | ||
} | ||
@@ -26,5 +37,14 @@ } | ||
var arrayToObject = function arrayToObject(source, options) { | ||
var obj = options && options.plainObjects ? Object.create(null) : {}; | ||
for (var i = 0; i < source.length; ++i) { | ||
if (typeof source[i] !== 'undefined') { | ||
obj[i] = source[i]; | ||
} | ||
} | ||
exports.merge = function (target, source, options) { | ||
return obj; | ||
}; | ||
var merge = function merge(target, source, options) { | ||
if (!source) { | ||
@@ -37,9 +57,9 @@ return target; | ||
target.push(source); | ||
} else if (typeof target === 'object') { | ||
if (options.plainObjects || options.allowPrototypes || !has.call(Object.prototype, source)) { | ||
target[source] = true; | ||
} | ||
} else { | ||
return [target, source]; | ||
} | ||
else if (typeof target === 'object') { | ||
target[source] = true; | ||
} | ||
else { | ||
target = [target, source]; | ||
} | ||
@@ -50,31 +70,45 @@ return target; | ||
if (typeof target !== 'object') { | ||
target = [target].concat(source); | ||
return target; | ||
return [target].concat(source); | ||
} | ||
if (Array.isArray(target) && | ||
!Array.isArray(source)) { | ||
var mergeTarget = target; | ||
if (Array.isArray(target) && !Array.isArray(source)) { | ||
mergeTarget = arrayToObject(target, options); | ||
} | ||
target = exports.arrayToObject(target, options); | ||
if (Array.isArray(target) && Array.isArray(source)) { | ||
source.forEach(function (item, i) { | ||
if (has.call(target, i)) { | ||
if (target[i] && typeof target[i] === 'object') { | ||
target[i] = merge(target[i], item, options); | ||
} else { | ||
target.push(item); | ||
} | ||
} else { | ||
target[i] = item; | ||
} | ||
}); | ||
return target; | ||
} | ||
var keys = Object.keys(source); | ||
for (var k = 0, kl = keys.length; k < kl; ++k) { | ||
var key = keys[k]; | ||
return Object.keys(source).reduce(function (acc, key) { | ||
var value = source[key]; | ||
if (!Object.prototype.hasOwnProperty.call(target, key)) { | ||
target[key] = value; | ||
if (has.call(acc, key)) { | ||
acc[key] = merge(acc[key], value, options); | ||
} else { | ||
acc[key] = value; | ||
} | ||
else { | ||
target[key] = exports.merge(target[key], value, options); | ||
} | ||
} | ||
return acc; | ||
}, mergeTarget); | ||
}; | ||
return target; | ||
var assign = function assignSingleSource(target, source) { | ||
return Object.keys(source).reduce(function (acc, key) { | ||
acc[key] = source[key]; | ||
return acc; | ||
}, target); | ||
}; | ||
exports.decode = function (str) { | ||
var decode = function (str) { | ||
try { | ||
@@ -87,4 +121,3 @@ return decodeURIComponent(str.replace(/\+/g, ' ')); | ||
exports.encode = function (str) { | ||
var encode = function encode(str) { | ||
// This code was originally written by Brian White (mscdex) for the io.js core querystring library. | ||
@@ -96,19 +129,18 @@ // It has been adapted here for stricter adherence to RFC 3986 | ||
if (typeof str !== 'string') { | ||
str = '' + str; | ||
} | ||
var string = typeof str === 'string' ? str : String(str); | ||
var out = ''; | ||
for (var i = 0, il = str.length; i < il; ++i) { | ||
var c = str.charCodeAt(i); | ||
for (var i = 0; i < string.length; ++i) { | ||
var c = string.charCodeAt(i); | ||
if (c === 0x2D || // - | ||
c === 0x2E || // . | ||
c === 0x5F || // _ | ||
c === 0x7E || // ~ | ||
(c >= 0x30 && c <= 0x39) || // 0-9 | ||
(c >= 0x41 && c <= 0x5A) || // a-z | ||
(c >= 0x61 && c <= 0x7A)) { // A-Z | ||
out += str[i]; | ||
if ( | ||
c === 0x2D // - | ||
|| c === 0x2E // . | ||
|| c === 0x5F // _ | ||
|| c === 0x7E // ~ | ||
|| (c >= 0x30 && c <= 0x39) // 0-9 | ||
|| (c >= 0x41 && c <= 0x5A) // a-z | ||
|| (c >= 0x61 && c <= 0x7A) // A-Z | ||
) { | ||
out += string.charAt(i); | ||
continue; | ||
@@ -118,3 +150,3 @@ } | ||
if (c < 0x80) { | ||
out += internals.hexTable[c]; | ||
out = out + hexTable[c]; | ||
continue; | ||
@@ -124,3 +156,3 @@ } | ||
if (c < 0x800) { | ||
out += internals.hexTable[0xC0 | (c >> 6)] + internals.hexTable[0x80 | (c & 0x3F)]; | ||
out = out + (hexTable[0xC0 | (c >> 6)] + hexTable[0x80 | (c & 0x3F)]); | ||
continue; | ||
@@ -130,9 +162,12 @@ } | ||
if (c < 0xD800 || c >= 0xE000) { | ||
out += internals.hexTable[0xE0 | (c >> 12)] + internals.hexTable[0x80 | ((c >> 6) & 0x3F)] + internals.hexTable[0x80 | (c & 0x3F)]; | ||
out = out + (hexTable[0xE0 | (c >> 12)] + hexTable[0x80 | ((c >> 6) & 0x3F)] + hexTable[0x80 | (c & 0x3F)]); | ||
continue; | ||
} | ||
++i; | ||
c = 0x10000 + (((c & 0x3FF) << 10) | (str.charCodeAt(i) & 0x3FF)); | ||
out += internals.hexTable[0xF0 | (c >> 18)] + internals.hexTable[0x80 | ((c >> 12) & 0x3F)] + internals.hexTable[0x80 | ((c >> 6) & 0x3F)] + internals.hexTable[0x80 | (c & 0x3F)]; | ||
i += 1; | ||
c = 0x10000 + (((c & 0x3FF) << 10) | (string.charCodeAt(i) & 0x3FF)); | ||
out += hexTable[0xF0 | (c >> 18)] | ||
+ hexTable[0x80 | ((c >> 12) & 0x3F)] | ||
+ hexTable[0x80 | ((c >> 6) & 0x3F)] | ||
+ hexTable[0x80 | (c & 0x3F)]; | ||
} | ||
@@ -143,57 +178,45 @@ | ||
exports.compact = function (obj, refs) { | ||
var compact = function compact(value) { | ||
var queue = [{ obj: { o: value }, prop: 'o' }]; | ||
var refs = []; | ||
if (typeof obj !== 'object' || | ||
obj === null) { | ||
for (var i = 0; i < queue.length; ++i) { | ||
var item = queue[i]; | ||
var obj = item.obj[item.prop]; | ||
return obj; | ||
} | ||
refs = refs || []; | ||
var lookup = refs.indexOf(obj); | ||
if (lookup !== -1) { | ||
return refs[lookup]; | ||
} | ||
refs.push(obj); | ||
if (Array.isArray(obj)) { | ||
var compacted = []; | ||
for (var i = 0, il = obj.length; i < il; ++i) { | ||
if (typeof obj[i] !== 'undefined') { | ||
compacted.push(obj[i]); | ||
var keys = Object.keys(obj); | ||
for (var j = 0; j < keys.length; ++j) { | ||
var key = keys[j]; | ||
var val = obj[key]; | ||
if (typeof val === 'object' && val !== null && refs.indexOf(val) === -1) { | ||
queue.push({ obj: obj, prop: key }); | ||
refs.push(val); | ||
} | ||
} | ||
return compacted; | ||
} | ||
var keys = Object.keys(obj); | ||
for (i = 0, il = keys.length; i < il; ++i) { | ||
var key = keys[i]; | ||
obj[key] = exports.compact(obj[key], refs); | ||
} | ||
return obj; | ||
return compactQueue(queue); | ||
}; | ||
exports.isRegExp = function (obj) { | ||
var isRegExp = function isRegExp(obj) { | ||
return Object.prototype.toString.call(obj) === '[object RegExp]'; | ||
}; | ||
exports.isBuffer = function (obj) { | ||
if (obj === null || | ||
typeof obj === 'undefined') { | ||
var isBuffer = function isBuffer(obj) { | ||
if (obj === null || typeof obj === 'undefined') { | ||
return false; | ||
} | ||
return !!(obj.constructor && | ||
obj.constructor.isBuffer && | ||
obj.constructor.isBuffer(obj)); | ||
return !!(obj.constructor && obj.constructor.isBuffer && obj.constructor.isBuffer(obj)); | ||
}; | ||
module.exports = { | ||
arrayToObject: arrayToObject, | ||
assign: assign, | ||
compact: compact, | ||
decode: decode, | ||
encode: encode, | ||
isBuffer: isBuffer, | ||
isRegExp: isRegExp, | ||
merge: merge | ||
}; |
@@ -5,8 +5,8 @@ { | ||
{ | ||
"raw": "qs@^5.2.0", | ||
"raw": "qs@^6.5.2", | ||
"scope": null, | ||
"escapedName": "qs", | ||
"name": "qs", | ||
"rawSpec": "^5.2.0", | ||
"spec": ">=5.2.0 <6.0.0", | ||
"rawSpec": "^6.5.2", | ||
"spec": ">=6.5.2 <7.0.0", | ||
"type": "range" | ||
@@ -17,10 +17,10 @@ }, | ||
], | ||
"_from": "qs@>=5.2.0 <6.0.0", | ||
"_id": "qs@5.2.1", | ||
"_from": "qs@>=6.5.2 <7.0.0", | ||
"_id": "qs@6.5.2", | ||
"_inCache": true, | ||
"_location": "/qs", | ||
"_nodeVersion": "6.3.0", | ||
"_nodeVersion": "10.0.0", | ||
"_npmOperationalInternal": { | ||
"host": "packages-12-west.internal.npmjs.com", | ||
"tmp": "tmp/qs-5.2.1.tgz_1469043460945_0.553046926856041" | ||
"host": "s3://npm-registry-packages", | ||
"tmp": "tmp/qs_6.5.2_1525413973776_0.4130968177833936" | ||
}, | ||
@@ -31,39 +31,62 @@ "_npmUser": { | ||
}, | ||
"_npmVersion": "3.10.3", | ||
"_npmVersion": "5.6.0", | ||
"_phantomChildren": {}, | ||
"_requested": { | ||
"raw": "qs@^5.2.0", | ||
"raw": "qs@^6.5.2", | ||
"scope": null, | ||
"escapedName": "qs", | ||
"name": "qs", | ||
"rawSpec": "^5.2.0", | ||
"spec": ">=5.2.0 <6.0.0", | ||
"rawSpec": "^6.5.2", | ||
"spec": ">=6.5.2 <7.0.0", | ||
"type": "range" | ||
}, | ||
"_requiredBy": [ | ||
"/request", | ||
"/webpack-combine-loaders" | ||
], | ||
"_resolved": "http://registry.npmjs.org/qs/-/qs-5.2.1.tgz", | ||
"_shasum": "801fee030e0b9450d6385adc48a4cc55b44aedfc", | ||
"_resolved": "http://registry.npmjs.org/qs/-/qs-6.5.2.tgz", | ||
"_shasum": "cb3ae806e8740444584ef154ce8ee98d403f3e36", | ||
"_shrinkwrap": null, | ||
"_spec": "qs@^5.2.0", | ||
"_spec": "qs@^6.5.2", | ||
"_where": "/home/travis/build/kentcdodds/webpack-config-utils/node_modules/webpack-combine-loaders", | ||
"bugs": { | ||
"url": "https://github.com/hapijs/qs/issues" | ||
"url": "https://github.com/ljharb/qs/issues" | ||
}, | ||
"contributors": [ | ||
{ | ||
"name": "Jordan Harband", | ||
"email": "ljharb@gmail.com", | ||
"url": "http://ljharb.codes" | ||
} | ||
], | ||
"dependencies": {}, | ||
"description": "A querystring parser that supports nesting and arrays, with a depth limit", | ||
"devDependencies": { | ||
"browserify": "^10.2.1", | ||
"code": "1.x.x", | ||
"lab": "5.x.x" | ||
"@ljharb/eslint-config": "^12.2.1", | ||
"browserify": "^16.2.0", | ||
"covert": "^1.1.0", | ||
"editorconfig-tools": "^0.1.1", | ||
"eslint": "^4.19.1", | ||
"evalmd": "^0.0.17", | ||
"iconv-lite": "^0.4.21", | ||
"mkdirp": "^0.5.1", | ||
"qs-iconv": "^1.0.4", | ||
"safe-publish-latest": "^1.1.1", | ||
"safer-buffer": "^2.1.2", | ||
"tape": "^4.9.0" | ||
}, | ||
"directories": {}, | ||
"dist": { | ||
"shasum": "801fee030e0b9450d6385adc48a4cc55b44aedfc", | ||
"tarball": "https://registry.npmjs.org/qs/-/qs-5.2.1.tgz" | ||
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", | ||
"shasum": "cb3ae806e8740444584ef154ce8ee98d403f3e36", | ||
"tarball": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", | ||
"fileCount": 18, | ||
"unpackedSize": 114127, | ||
"npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa6/hXCRA9TVsSAnZWagAAEJQP/0c3B2SWDmhz9zC9na++\nc6v1YkoIPJ+IhVaFhrpKvT3HeMsRbdQFTjGJ+7VijncHdxuieAGSAo5Tm9MZ\nnrskliXl61TelWC3/1GJ7hzggBCIudwf1Kl5P2lx+XLTBag+B3BnfxR+Gp/o\nF8f+CJ3I2KxkKZWcHYBTLLgE5dPi5i5KFnh9EAsaZRUiowCDl+CaKzj3u55M\nUfwKrUXLfZtgL3ALj5Nh/g1F+NW7m7RIDREVFPJ3MqeiNUXUnGU9KBmQuHMm\nWvOMuvN9vgPm40fp9TBZQlEazaR12QBqqvpgDtYBNqfUQaznoyZ8rN36W1oB\n5iV3Ve6B+BeIPNMaf92TvAnbvLcAEfy36VuOiH3IHDyEDAk706n/etInNeYS\nK/N359uzbRCQ7MQTJ5Z9TmlsD6/TFRfcBcGUrnPoL6HEEIxmAnJbKApy1sP4\nqb/GnrOQMuRJ6ty58aTEzotFvsxFw8kaeeexZ7Ftd7cKYdBRjutfiibJTH3v\n8feuHS0wDXRDvVd906qwYvC4LwGeqTJeRUHtFrd3pwCDqiXGyEXP60Yyir6L\nqZIYxzv8FRBspN//x5YJndpU06joflcCmm0bLfu+zLSevL4eciH+ciBQZQVY\nzWdcWZktjGXNt5I1s7xO31sgV7nwiXSPUQDS/SuaE860GWQ9pp0yL9M23S/q\ny88W\r\n=156t\r\n-----END PGP SIGNATURE-----\r\n" | ||
}, | ||
"engines": ">=0.10.40", | ||
"gitHead": "872da25efd167985c153898a06277ef34ce97a63", | ||
"homepage": "https://github.com/hapijs/qs", | ||
"engines": { | ||
"node": ">=0.6" | ||
}, | ||
"gitHead": "eaabd05558b657c75a137caf2eb030e8e856b82f", | ||
"homepage": "https://github.com/ljharb/qs", | ||
"keywords": [ | ||
@@ -94,11 +117,16 @@ "querystring", | ||
"type": "git", | ||
"url": "git+https://github.com/hapijs/qs.git" | ||
"url": "git+https://github.com/ljharb/qs.git" | ||
}, | ||
"scripts": { | ||
"dist": "browserify --standalone Qs lib/index.js > dist/qs.js", | ||
"test": "lab -a code -t 100 -L", | ||
"test-cov-html": "lab -a code -r html -o coverage.html", | ||
"test-tap": "lab -a code -r tap -o tests.tap" | ||
"coverage": "covert test", | ||
"dist": "mkdirp dist && browserify --standalone Qs lib/index.js > dist/qs.js", | ||
"lint": "eslint lib/*.js test/*.js", | ||
"prelint": "editorconfig-tools check * lib/* test/*", | ||
"prepublish": "safe-publish-latest && npm run dist", | ||
"pretest": "npm run --silent readme && npm run --silent lint", | ||
"readme": "evalmd README.md", | ||
"test": "npm run --silent coverage", | ||
"tests-only": "node test" | ||
}, | ||
"version": "5.2.1" | ||
"version": "6.5.2" | ||
} |
@@ -1,9 +0,15 @@ | ||
# qs | ||
# qs <sup>[![Version Badge][2]][1]</sup> | ||
[![Build Status][3]][4] | ||
[![dependency status][5]][6] | ||
[![dev dependency status][7]][8] | ||
[![License][license-image]][license-url] | ||
[![Downloads][downloads-image]][downloads-url] | ||
[![npm badge][11]][1] | ||
A querystring parsing and stringifying library with some added security. | ||
[![Build Status](https://secure.travis-ci.org/hapijs/qs.svg)](http://travis-ci.org/hapijs/qs) | ||
Lead Maintainer: [Jordan Harband](https://github.com/ljharb) | ||
Lead Maintainer: [Nathan LaFreniere](https://github.com/nlf) | ||
The **qs** module was originally created and maintained by [TJ Holowaychuk](https://github.com/visionmedia/node-querystring). | ||
@@ -14,6 +20,10 @@ | ||
```javascript | ||
var Qs = require('qs'); | ||
var qs = require('qs'); | ||
var assert = require('assert'); | ||
var obj = Qs.parse('a=c'); // { a: 'c' } | ||
var str = Qs.stringify(obj); // 'a=c' | ||
var obj = qs.parse('a=c'); | ||
assert.deepEqual(obj, { a: 'c' }); | ||
var str = qs.stringify(obj); | ||
assert.equal(str, 'a=c'); | ||
``` | ||
@@ -23,4 +33,5 @@ | ||
[](#preventEval) | ||
```javascript | ||
Qs.parse(string, [options]); | ||
qs.parse(string, [options]); | ||
``` | ||
@@ -32,14 +43,14 @@ | ||
```javascript | ||
{ | ||
foo: { | ||
bar: 'baz' | ||
} | ||
} | ||
assert.deepEqual(qs.parse('foo[bar]=baz'), { | ||
foo: { | ||
bar: 'baz' | ||
} | ||
}); | ||
``` | ||
When using the `plainObjects` option the parsed value is returned as a plain object, created via `Object.create(null)` and as such you should be aware that prototype methods will not exist on it and a user may set those names to whatever value they like: | ||
When using the `plainObjects` option the parsed value is returned as a null object, created via `Object.create(null)` and as such you should be aware that prototype methods will not exist on it and a user may set those names to whatever value they like: | ||
```javascript | ||
Qs.parse('a.hasOwnProperty=b', { plainObjects: true }); | ||
// { a: { hasOwnProperty: 'b' } } | ||
var nullObject = qs.parse('a[hasOwnProperty]=b', { plainObjects: true }); | ||
assert.deepEqual(nullObject, { a: { hasOwnProperty: 'b' } }); | ||
``` | ||
@@ -50,4 +61,4 @@ | ||
```javascript | ||
Qs.parse('a.hasOwnProperty=b', { allowPrototypes: true }); | ||
// { a: { hasOwnProperty: 'b' } } | ||
var protoObject = qs.parse('a[hasOwnProperty]=b', { allowPrototypes: true }); | ||
assert.deepEqual(protoObject, { a: { hasOwnProperty: 'b' } }); | ||
``` | ||
@@ -58,4 +69,5 @@ | ||
```javascript | ||
Qs.parse('a%5Bb%5D=c'); | ||
// { a: { b: 'c' } } | ||
assert.deepEqual(qs.parse('a%5Bb%5D=c'), { | ||
a: { b: 'c' } | ||
}); | ||
``` | ||
@@ -66,9 +78,9 @@ | ||
```javascript | ||
{ | ||
foo: { | ||
bar: { | ||
baz: 'foobarbaz' | ||
assert.deepEqual(qs.parse('foo[bar][baz]=foobarbaz'), { | ||
foo: { | ||
bar: { | ||
baz: 'foobarbaz' | ||
} | ||
} | ||
} | ||
} | ||
}); | ||
``` | ||
@@ -80,24 +92,26 @@ | ||
```javascript | ||
{ | ||
a: { | ||
b: { | ||
c: { | ||
d: { | ||
e: { | ||
f: { | ||
'[g][h][i]': 'j' | ||
var expected = { | ||
a: { | ||
b: { | ||
c: { | ||
d: { | ||
e: { | ||
f: { | ||
'[g][h][i]': 'j' | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
var string = 'a[b][c][d][e][f][g][h][i]=j'; | ||
assert.deepEqual(qs.parse(string), expected); | ||
``` | ||
This depth can be overridden by passing a `depth` option to `Qs.parse(string, [options])`: | ||
This depth can be overridden by passing a `depth` option to `qs.parse(string, [options])`: | ||
```javascript | ||
Qs.parse('a[b][c][d][e][f][g][h][i]=j', { depth: 1 }); | ||
// { a: { b: { '[c][d][e][f][g][h][i]': 'j' } } } | ||
var deep = qs.parse('a[b][c][d][e][f][g][h][i]=j', { depth: 1 }); | ||
assert.deepEqual(deep, { a: { b: { '[c][d][e][f][g][h][i]': 'j' } } }); | ||
``` | ||
@@ -110,11 +124,18 @@ | ||
```javascript | ||
Qs.parse('a=b&c=d', { parameterLimit: 1 }); | ||
// { a: 'b' } | ||
var limited = qs.parse('a=b&c=d', { parameterLimit: 1 }); | ||
assert.deepEqual(limited, { a: 'b' }); | ||
``` | ||
To bypass the leading question mark, use `ignoreQueryPrefix`: | ||
```javascript | ||
var prefixed = qs.parse('?a=b&c=d', { ignoreQueryPrefix: true }); | ||
assert.deepEqual(prefixed, { a: 'b', c: 'd' }); | ||
``` | ||
An optional delimiter can also be passed: | ||
```javascript | ||
Qs.parse('a=b;c=d', { delimiter: ';' }); | ||
// { a: 'b', c: 'd' } | ||
var delimited = qs.parse('a=b;c=d', { delimiter: ';' }); | ||
assert.deepEqual(delimited, { a: 'b', c: 'd' }); | ||
``` | ||
@@ -125,4 +146,4 @@ | ||
```javascript | ||
Qs.parse('a=b;c=d,e=f', { delimiter: /[;,]/ }); | ||
// { a: 'b', c: 'd', e: 'f' } | ||
var regexed = qs.parse('a=b;c=d,e=f', { delimiter: /[;,]/ }); | ||
assert.deepEqual(regexed, { a: 'b', c: 'd', e: 'f' }); | ||
``` | ||
@@ -133,4 +154,4 @@ | ||
```javascript | ||
Qs.parse('a.b=c', { allowDots: true }); | ||
// { a: { b: 'c' } } | ||
var withDots = qs.parse('a.b=c', { allowDots: true }); | ||
assert.deepEqual(withDots, { a: { b: 'c' } }); | ||
``` | ||
@@ -143,4 +164,4 @@ | ||
```javascript | ||
Qs.parse('a[]=b&a[]=c'); | ||
// { a: ['b', 'c'] } | ||
var withArray = qs.parse('a[]=b&a[]=c'); | ||
assert.deepEqual(withArray, { a: ['b', 'c'] }); | ||
``` | ||
@@ -151,4 +172,4 @@ | ||
```javascript | ||
Qs.parse('a[1]=c&a[0]=b'); | ||
// { a: ['b', 'c'] } | ||
var withIndexes = qs.parse('a[1]=c&a[0]=b'); | ||
assert.deepEqual(withIndexes, { a: ['b', 'c'] }); | ||
``` | ||
@@ -161,4 +182,4 @@ | ||
```javascript | ||
Qs.parse('a[1]=b&a[15]=c'); | ||
// { a: ['b', 'c'] } | ||
var noSparse = qs.parse('a[1]=b&a[15]=c'); | ||
assert.deepEqual(noSparse, { a: ['b', 'c'] }); | ||
``` | ||
@@ -169,6 +190,7 @@ | ||
```javascript | ||
Qs.parse('a[]=&a[]=b'); | ||
// { a: ['', 'b'] } | ||
Qs.parse('a[0]=b&a[1]=&a[2]=c'); | ||
// { a: ['b', '', 'c'] } | ||
var withEmptyString = qs.parse('a[]=&a[]=b'); | ||
assert.deepEqual(withEmptyString, { a: ['', 'b'] }); | ||
var withIndexedEmptyString = qs.parse('a[0]=b&a[1]=&a[2]=c'); | ||
assert.deepEqual(withIndexedEmptyString, { a: ['b', '', 'c'] }); | ||
``` | ||
@@ -180,4 +202,4 @@ | ||
```javascript | ||
Qs.parse('a[100]=b'); | ||
// { a: { '100': 'b' } } | ||
var withMaxIndex = qs.parse('a[100]=b'); | ||
assert.deepEqual(withMaxIndex, { a: { '100': 'b' } }); | ||
``` | ||
@@ -188,4 +210,4 @@ | ||
```javascript | ||
Qs.parse('a[1]=b', { arrayLimit: 0 }); | ||
// { a: { '1': 'b' } } | ||
var withArrayLimit = qs.parse('a[1]=b', { arrayLimit: 0 }); | ||
assert.deepEqual(withArrayLimit, { a: { '1': 'b' } }); | ||
``` | ||
@@ -196,4 +218,4 @@ | ||
```javascript | ||
Qs.parse('a[]=b', { parseArrays: false }); | ||
// { a: { '0': 'b' } } | ||
var noParsingArrays = qs.parse('a[]=b', { parseArrays: false }); | ||
assert.deepEqual(noParsingArrays, { a: { '0': 'b' } }); | ||
``` | ||
@@ -204,4 +226,4 @@ | ||
```javascript | ||
Qs.parse('a[0]=b&a[b]=c'); | ||
// { a: { '0': 'b', b: 'c' } } | ||
var mixedNotation = qs.parse('a[0]=b&a[b]=c'); | ||
assert.deepEqual(mixedNotation, { a: { '0': 'b', b: 'c' } }); | ||
``` | ||
@@ -212,4 +234,4 @@ | ||
```javascript | ||
Qs.parse('a[][b]=c'); | ||
// { a: [{ b: 'c' }] } | ||
var arraysOfObjects = qs.parse('a[][b]=c'); | ||
assert.deepEqual(arraysOfObjects, { a: [{ b: 'c' }] }); | ||
``` | ||
@@ -219,4 +241,5 @@ | ||
[](#preventEval) | ||
```javascript | ||
Qs.stringify(object, [options]); | ||
qs.stringify(object, [options]); | ||
``` | ||
@@ -227,6 +250,4 @@ | ||
```javascript | ||
Qs.stringify({ a: 'b' }); | ||
// 'a=b' | ||
Qs.stringify({ a: { b: 'c' } }); | ||
// 'a%5Bb%5D=c' | ||
assert.equal(qs.stringify({ a: 'b' }), 'a=b'); | ||
assert.equal(qs.stringify({ a: { b: 'c' } }), 'a%5Bb%5D=c'); | ||
``` | ||
@@ -237,6 +258,35 @@ | ||
```javascript | ||
Qs.stringify({ a: { b: 'c' } }, { encode: false }); | ||
// 'a[b]=c' | ||
var unencoded = qs.stringify({ a: { b: 'c' } }, { encode: false }); | ||
assert.equal(unencoded, 'a[b]=c'); | ||
``` | ||
Encoding can be disabled for keys by setting the `encodeValuesOnly` option to `true`: | ||
```javascript | ||
var encodedValues = qs.stringify( | ||
{ a: 'b', c: ['d', 'e=f'], f: [['g'], ['h']] }, | ||
{ encodeValuesOnly: true } | ||
); | ||
assert.equal(encodedValues,'a=b&c[0]=d&c[1]=e%3Df&f[0][0]=g&f[1][0]=h'); | ||
``` | ||
This encoding can also be replaced by a custom encoding method set as `encoder` option: | ||
```javascript | ||
var encoded = qs.stringify({ a: { b: 'c' } }, { encoder: function (str) { | ||
// Passed in values `a`, `b`, `c` | ||
return // Return encoded string | ||
}}) | ||
``` | ||
_(Note: the `encoder` option does not apply if `encode` is `false`)_ | ||
Analogue to the `encoder` there is a `decoder` option for `parse` to override decoding of properties and values: | ||
```javascript | ||
var decoded = qs.parse('x=z', { decoder: function (str) { | ||
// Passed in values `x`, `z` | ||
return // Return decoded string | ||
}}) | ||
``` | ||
Examples beyond this point will be shown as though the output is not URI encoded for clarity. Please note that the return values in these cases *will* be URI encoded during real usage. | ||
@@ -247,3 +297,3 @@ | ||
```javascript | ||
Qs.stringify({ a: ['b', 'c', 'd'] }); | ||
qs.stringify({ a: ['b', 'c', 'd'] }); | ||
// 'a[0]=b&a[1]=c&a[2]=d' | ||
@@ -255,38 +305,85 @@ ``` | ||
```javascript | ||
Qs.stringify({ a: ['b', 'c', 'd'] }, { indices: false }); | ||
qs.stringify({ a: ['b', 'c', 'd'] }, { indices: false }); | ||
// 'a=b&a=c&a=d' | ||
``` | ||
You may use the `arrayFormat` option to specify the format of the output array | ||
You may use the `arrayFormat` option to specify the format of the output array: | ||
```javascript | ||
Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' }) | ||
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' }) | ||
// 'a[0]=b&a[1]=c' | ||
Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' }) | ||
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' }) | ||
// 'a[]=b&a[]=c' | ||
Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' }) | ||
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' }) | ||
// 'a=b&a=c' | ||
``` | ||
When objects are stringified, by default they use bracket notation: | ||
```javascript | ||
qs.stringify({ a: { b: { c: 'd', e: 'f' } } }); | ||
// 'a[b][c]=d&a[b][e]=f' | ||
``` | ||
You may override this to use dot notation by setting the `allowDots` option to `true`: | ||
```javascript | ||
qs.stringify({ a: { b: { c: 'd', e: 'f' } } }, { allowDots: true }); | ||
// 'a.b.c=d&a.b.e=f' | ||
``` | ||
Empty strings and null values will omit the value, but the equals sign (=) remains in place: | ||
```javascript | ||
Qs.stringify({ a: '' }); | ||
// 'a=' | ||
assert.equal(qs.stringify({ a: '' }), 'a='); | ||
``` | ||
Key with no values (such as an empty object or array) will return nothing: | ||
```javascript | ||
assert.equal(qs.stringify({ a: [] }), ''); | ||
assert.equal(qs.stringify({ a: {} }), ''); | ||
assert.equal(qs.stringify({ a: [{}] }), ''); | ||
assert.equal(qs.stringify({ a: { b: []} }), ''); | ||
assert.equal(qs.stringify({ a: { b: {}} }), ''); | ||
``` | ||
Properties that are set to `undefined` will be omitted entirely: | ||
```javascript | ||
Qs.stringify({ a: null, b: undefined }); | ||
// 'a=' | ||
assert.equal(qs.stringify({ a: null, b: undefined }), 'a='); | ||
``` | ||
The query string may optionally be prepended with a question mark: | ||
```javascript | ||
assert.equal(qs.stringify({ a: 'b', c: 'd' }, { addQueryPrefix: true }), '?a=b&c=d'); | ||
``` | ||
The delimiter may be overridden with stringify as well: | ||
```javascript | ||
Qs.stringify({ a: 'b', c: 'd' }, { delimiter: ';' }); | ||
// 'a=b;c=d' | ||
assert.equal(qs.stringify({ a: 'b', c: 'd' }, { delimiter: ';' }), 'a=b;c=d'); | ||
``` | ||
If you only want to override the serialization of `Date` objects, you can provide a `serializeDate` option: | ||
```javascript | ||
var date = new Date(7); | ||
assert.equal(qs.stringify({ a: date }), 'a=1970-01-01T00:00:00.007Z'.replace(/:/g, '%3A')); | ||
assert.equal( | ||
qs.stringify({ a: date }, { serializeDate: function (d) { return d.getTime(); } }), | ||
'a=7' | ||
); | ||
``` | ||
You may use the `sort` option to affect the order of parameter keys: | ||
```javascript | ||
function alphabeticalSort(a, b) { | ||
return a.localeCompare(b); | ||
} | ||
assert.equal(qs.stringify({ a: 'c', z: 'y', b : 'f' }, { sort: alphabeticalSort }), 'a=c&b=f&z=y'); | ||
``` | ||
Finally, you can use the `filter` option to restrict which keys will be included in the stringified output. | ||
@@ -298,19 +395,19 @@ If you pass a function, it will be called for each key to obtain the replacement value. Otherwise, if you | ||
function filterFunc(prefix, value) { | ||
if (prefix == 'b') { | ||
// Return an `undefined` value to omit a property. | ||
return; | ||
} | ||
if (prefix == 'e[f]') { | ||
return value.getTime(); | ||
} | ||
if (prefix == 'e[g][0]') { | ||
return value * 2; | ||
} | ||
return value; | ||
if (prefix == 'b') { | ||
// Return an `undefined` value to omit a property. | ||
return; | ||
} | ||
if (prefix == 'e[f]') { | ||
return value.getTime(); | ||
} | ||
if (prefix == 'e[g][0]') { | ||
return value * 2; | ||
} | ||
return value; | ||
} | ||
Qs.stringify({ a: 'b', c: 'd', e: { f: new Date(123), g: [2] } }, { filter: filterFunc }) | ||
qs.stringify({ a: 'b', c: 'd', e: { f: new Date(123), g: [2] } }, { filter: filterFunc }); | ||
// 'a=b&c=d&e[f]=123&e[g][0]=4' | ||
Qs.stringify({ a: 'b', c: 'd', e: 'f' }, { filter: ['a', 'e'] }) | ||
qs.stringify({ a: 'b', c: 'd', e: 'f' }, { filter: ['a', 'e'] }); | ||
// 'a=b&e=f' | ||
Qs.stringify({ a: ['b', 'c', 'd'], e: 'f' }, { filter: ['a', 0, 2] }) | ||
qs.stringify({ a: ['b', 'c', 'd'], e: 'f' }, { filter: ['a', 0, 2] }); | ||
// 'a[0]=b&a[2]=d' | ||
@@ -324,4 +421,4 @@ ``` | ||
```javascript | ||
Qs.stringify({ a: null, b: '' }); | ||
// 'a=&b=' | ||
var withNull = qs.stringify({ a: null, b: '' }); | ||
assert.equal(withNull, 'a=&b='); | ||
``` | ||
@@ -332,4 +429,4 @@ | ||
```javascript | ||
Qs.parse('a&b=') | ||
// { a: '', b: '' } | ||
var equalsInsensitive = qs.parse('a&b='); | ||
assert.deepEqual(equalsInsensitive, { a: '', b: '' }); | ||
``` | ||
@@ -341,4 +438,4 @@ | ||
```javascript | ||
Qs.stringify({ a: null, b: '' }, { strictNullHandling: true }); | ||
// 'a&b=' | ||
var strictNull = qs.stringify({ a: null, b: '' }, { strictNullHandling: true }); | ||
assert.equal(strictNull, 'a&b='); | ||
``` | ||
@@ -349,5 +446,4 @@ | ||
```javascript | ||
Qs.parse('a&b=', { strictNullHandling: true }); | ||
// { a: null, b: '' } | ||
var parsedStrictNull = qs.parse('a&b=', { strictNullHandling: true }); | ||
assert.deepEqual(parsedStrictNull, { a: null, b: '' }); | ||
``` | ||
@@ -358,4 +454,52 @@ | ||
```javascript | ||
qs.stringify({ a: 'b', c: null}, { skipNulls: true }) | ||
// 'a=b' | ||
var nullsSkipped = qs.stringify({ a: 'b', c: null}, { skipNulls: true }); | ||
assert.equal(nullsSkipped, 'a=b'); | ||
``` | ||
### Dealing with special character sets | ||
By default the encoding and decoding of characters is done in `utf-8`. If you | ||
wish to encode querystrings to a different character set (i.e. | ||
[Shift JIS](https://en.wikipedia.org/wiki/Shift_JIS)) you can use the | ||
[`qs-iconv`](https://github.com/martinheidegger/qs-iconv) library: | ||
```javascript | ||
var encoder = require('qs-iconv/encoder')('shift_jis'); | ||
var shiftJISEncoded = qs.stringify({ a: 'γγγ«γ‘γ―οΌ' }, { encoder: encoder }); | ||
assert.equal(shiftJISEncoded, 'a=%82%B1%82%F1%82%C9%82%BF%82%CD%81I'); | ||
``` | ||
This also works for decoding of query strings: | ||
```javascript | ||
var decoder = require('qs-iconv/decoder')('shift_jis'); | ||
var obj = qs.parse('a=%82%B1%82%F1%82%C9%82%BF%82%CD%81I', { decoder: decoder }); | ||
assert.deepEqual(obj, { a: 'γγγ«γ‘γ―οΌ' }); | ||
``` | ||
### RFC 3986 and RFC 1738 space encoding | ||
RFC3986 used as default option and encodes ' ' to *%20* which is backward compatible. | ||
In the same time, output can be stringified as per RFC1738 with ' ' equal to '+'. | ||
``` | ||
assert.equal(qs.stringify({ a: 'b c' }), 'a=b%20c'); | ||
assert.equal(qs.stringify({ a: 'b c' }, { format : 'RFC3986' }), 'a=b%20c'); | ||
assert.equal(qs.stringify({ a: 'b c' }, { format : 'RFC1738' }), 'a=b+c'); | ||
``` | ||
[1]: https://npmjs.org/package/qs | ||
[2]: http://versionbadg.es/ljharb/qs.svg | ||
[3]: https://api.travis-ci.org/ljharb/qs.svg | ||
[4]: https://travis-ci.org/ljharb/qs | ||
[5]: https://david-dm.org/ljharb/qs.svg | ||
[6]: https://david-dm.org/ljharb/qs | ||
[7]: https://david-dm.org/ljharb/qs/dev-status.svg | ||
[8]: https://david-dm.org/ljharb/qs?type=dev | ||
[9]: https://ci.testling.com/ljharb/qs.png | ||
[10]: https://ci.testling.com/ljharb/qs | ||
[11]: https://nodei.co/npm/qs.png?downloads=true&stars=true | ||
[license-image]: http://img.shields.io/npm/l/qs.svg | ||
[license-url]: LICENSE | ||
[downloads-image]: http://img.shields.io/npm/dm/qs.svg | ||
[downloads-url]: http://npm-stat.com/charts.html?package=qs |
@@ -1,42 +0,27 @@ | ||
/* eslint no-extend-native:0 */ | ||
// Load modules | ||
'use strict'; | ||
var Code = require('code'); | ||
var Lab = require('lab'); | ||
var Qs = require('../'); | ||
var test = require('tape'); | ||
var qs = require('../'); | ||
var utils = require('../lib/utils'); | ||
var iconv = require('iconv-lite'); | ||
var SaferBuffer = require('safer-buffer').Buffer; | ||
// Declare internals | ||
var internals = {}; | ||
// Test shortcuts | ||
var lab = exports.lab = Lab.script(); | ||
var expect = Code.expect; | ||
var describe = lab.experiment; | ||
var it = lab.test; | ||
describe('parse()', function () { | ||
it('parses a simple string', function (done) { | ||
expect(Qs.parse('0=foo')).to.deep.equal({ '0': 'foo' }); | ||
expect(Qs.parse('foo=c++')).to.deep.equal({ foo: 'c ' }); | ||
expect(Qs.parse('a[>=]=23')).to.deep.equal({ a: { '>=': '23' } }); | ||
expect(Qs.parse('a[<=>]==23')).to.deep.equal({ a: { '<=>': '=23' } }); | ||
expect(Qs.parse('a[==]=23')).to.deep.equal({ a: { '==': '23' } }); | ||
expect(Qs.parse('foo', { strictNullHandling: true })).to.deep.equal({ foo: null }); | ||
expect(Qs.parse('foo' )).to.deep.equal({ foo: '' }); | ||
expect(Qs.parse('foo=')).to.deep.equal({ foo: '' }); | ||
expect(Qs.parse('foo=bar')).to.deep.equal({ foo: 'bar' }); | ||
expect(Qs.parse(' foo = bar = baz ')).to.deep.equal({ ' foo ': ' bar = baz ' }); | ||
expect(Qs.parse('foo=bar=baz')).to.deep.equal({ foo: 'bar=baz' }); | ||
expect(Qs.parse('foo=bar&bar=baz')).to.deep.equal({ foo: 'bar', bar: 'baz' }); | ||
expect(Qs.parse('foo2=bar2&baz2=')).to.deep.equal({ foo2: 'bar2', baz2: '' }); | ||
expect(Qs.parse('foo=bar&baz', { strictNullHandling: true })).to.deep.equal({ foo: 'bar', baz: null }); | ||
expect(Qs.parse('foo=bar&baz')).to.deep.equal({ foo: 'bar', baz: '' }); | ||
expect(Qs.parse('cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World')).to.deep.equal({ | ||
test('parse()', function (t) { | ||
t.test('parses a simple string', function (st) { | ||
st.deepEqual(qs.parse('0=foo'), { 0: 'foo' }); | ||
st.deepEqual(qs.parse('foo=c++'), { foo: 'c ' }); | ||
st.deepEqual(qs.parse('a[>=]=23'), { a: { '>=': '23' } }); | ||
st.deepEqual(qs.parse('a[<=>]==23'), { a: { '<=>': '=23' } }); | ||
st.deepEqual(qs.parse('a[==]=23'), { a: { '==': '23' } }); | ||
st.deepEqual(qs.parse('foo', { strictNullHandling: true }), { foo: null }); | ||
st.deepEqual(qs.parse('foo'), { foo: '' }); | ||
st.deepEqual(qs.parse('foo='), { foo: '' }); | ||
st.deepEqual(qs.parse('foo=bar'), { foo: 'bar' }); | ||
st.deepEqual(qs.parse(' foo = bar = baz '), { ' foo ': ' bar = baz ' }); | ||
st.deepEqual(qs.parse('foo=bar=baz'), { foo: 'bar=baz' }); | ||
st.deepEqual(qs.parse('foo=bar&bar=baz'), { foo: 'bar', bar: 'baz' }); | ||
st.deepEqual(qs.parse('foo2=bar2&baz2='), { foo2: 'bar2', baz2: '' }); | ||
st.deepEqual(qs.parse('foo=bar&baz', { strictNullHandling: true }), { foo: 'bar', baz: null }); | ||
st.deepEqual(qs.parse('foo=bar&baz'), { foo: 'bar', baz: '' }); | ||
st.deepEqual(qs.parse('cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World'), { | ||
cht: 'p3', | ||
@@ -47,303 +32,287 @@ chd: 't:60,40', | ||
}); | ||
done(); | ||
st.end(); | ||
}); | ||
it('allows enabling dot notation', function (done) { | ||
expect(Qs.parse('a.b=c')).to.deep.equal({ 'a.b': 'c' }); | ||
expect(Qs.parse('a.b=c', { allowDots: true })).to.deep.equal({ a: { b: 'c' } }); | ||
done(); | ||
t.test('allows enabling dot notation', function (st) { | ||
st.deepEqual(qs.parse('a.b=c'), { 'a.b': 'c' }); | ||
st.deepEqual(qs.parse('a.b=c', { allowDots: true }), { a: { b: 'c' } }); | ||
st.end(); | ||
}); | ||
it('parses a single nested string', function (done) { | ||
t.deepEqual(qs.parse('a[b]=c'), { a: { b: 'c' } }, 'parses a single nested string'); | ||
t.deepEqual(qs.parse('a[b][c]=d'), { a: { b: { c: 'd' } } }, 'parses a double nested string'); | ||
t.deepEqual( | ||
qs.parse('a[b][c][d][e][f][g][h]=i'), | ||
{ a: { b: { c: { d: { e: { f: { '[g][h]': 'i' } } } } } } }, | ||
'defaults to a depth of 5' | ||
); | ||
expect(Qs.parse('a[b]=c')).to.deep.equal({ a: { b: 'c' } }); | ||
done(); | ||
t.test('only parses one level when depth = 1', function (st) { | ||
st.deepEqual(qs.parse('a[b][c]=d', { depth: 1 }), { a: { b: { '[c]': 'd' } } }); | ||
st.deepEqual(qs.parse('a[b][c][d]=e', { depth: 1 }), { a: { b: { '[c][d]': 'e' } } }); | ||
st.end(); | ||
}); | ||
it('parses a double nested string', function (done) { | ||
t.deepEqual(qs.parse('a=b&a=c'), { a: ['b', 'c'] }, 'parses a simple array'); | ||
expect(Qs.parse('a[b][c]=d')).to.deep.equal({ a: { b: { c: 'd' } } }); | ||
done(); | ||
t.test('parses an explicit array', function (st) { | ||
st.deepEqual(qs.parse('a[]=b'), { a: ['b'] }); | ||
st.deepEqual(qs.parse('a[]=b&a[]=c'), { a: ['b', 'c'] }); | ||
st.deepEqual(qs.parse('a[]=b&a[]=c&a[]=d'), { a: ['b', 'c', 'd'] }); | ||
st.end(); | ||
}); | ||
it('defaults to a depth of 5', function (done) { | ||
t.test('parses a mix of simple and explicit arrays', function (st) { | ||
st.deepEqual(qs.parse('a=b&a[]=c'), { a: ['b', 'c'] }); | ||
st.deepEqual(qs.parse('a[]=b&a=c'), { a: ['b', 'c'] }); | ||
st.deepEqual(qs.parse('a[0]=b&a=c'), { a: ['b', 'c'] }); | ||
st.deepEqual(qs.parse('a=b&a[0]=c'), { a: ['b', 'c'] }); | ||
expect(Qs.parse('a[b][c][d][e][f][g][h]=i')).to.deep.equal({ a: { b: { c: { d: { e: { f: { '[g][h]': 'i' } } } } } } }); | ||
done(); | ||
}); | ||
st.deepEqual(qs.parse('a[1]=b&a=c', { arrayLimit: 20 }), { a: ['b', 'c'] }); | ||
st.deepEqual(qs.parse('a[]=b&a=c', { arrayLimit: 0 }), { a: ['b', 'c'] }); | ||
st.deepEqual(qs.parse('a[]=b&a=c'), { a: ['b', 'c'] }); | ||
it('only parses one level when depth = 1', function (done) { | ||
st.deepEqual(qs.parse('a=b&a[1]=c', { arrayLimit: 20 }), { a: ['b', 'c'] }); | ||
st.deepEqual(qs.parse('a=b&a[]=c', { arrayLimit: 0 }), { a: ['b', 'c'] }); | ||
st.deepEqual(qs.parse('a=b&a[]=c'), { a: ['b', 'c'] }); | ||
expect(Qs.parse('a[b][c]=d', { depth: 1 })).to.deep.equal({ a: { b: { '[c]': 'd' } } }); | ||
expect(Qs.parse('a[b][c][d]=e', { depth: 1 })).to.deep.equal({ a: { b: { '[c][d]': 'e' } } }); | ||
done(); | ||
st.end(); | ||
}); | ||
it('parses a simple array', function (done) { | ||
expect(Qs.parse('a=b&a=c')).to.deep.equal({ a: ['b', 'c'] }); | ||
done(); | ||
t.test('parses a nested array', function (st) { | ||
st.deepEqual(qs.parse('a[b][]=c&a[b][]=d'), { a: { b: ['c', 'd'] } }); | ||
st.deepEqual(qs.parse('a[>=]=25'), { a: { '>=': '25' } }); | ||
st.end(); | ||
}); | ||
it('parses an explicit array', function (done) { | ||
expect(Qs.parse('a[]=b')).to.deep.equal({ a: ['b'] }); | ||
expect(Qs.parse('a[]=b&a[]=c')).to.deep.equal({ a: ['b', 'c'] }); | ||
expect(Qs.parse('a[]=b&a[]=c&a[]=d')).to.deep.equal({ a: ['b', 'c', 'd'] }); | ||
done(); | ||
t.test('allows to specify array indices', function (st) { | ||
st.deepEqual(qs.parse('a[1]=c&a[0]=b&a[2]=d'), { a: ['b', 'c', 'd'] }); | ||
st.deepEqual(qs.parse('a[1]=c&a[0]=b'), { a: ['b', 'c'] }); | ||
st.deepEqual(qs.parse('a[1]=c', { arrayLimit: 20 }), { a: ['c'] }); | ||
st.deepEqual(qs.parse('a[1]=c', { arrayLimit: 0 }), { a: { 1: 'c' } }); | ||
st.deepEqual(qs.parse('a[1]=c'), { a: ['c'] }); | ||
st.end(); | ||
}); | ||
it('parses a mix of simple and explicit arrays', function (done) { | ||
expect(Qs.parse('a=b&a[]=c')).to.deep.equal({ a: ['b', 'c'] }); | ||
expect(Qs.parse('a[]=b&a=c')).to.deep.equal({ a: ['b', 'c'] }); | ||
expect(Qs.parse('a[0]=b&a=c')).to.deep.equal({ a: ['b', 'c'] }); | ||
expect(Qs.parse('a=b&a[0]=c')).to.deep.equal({ a: ['b', 'c'] }); | ||
expect(Qs.parse('a[1]=b&a=c')).to.deep.equal({ a: ['b', 'c'] }); | ||
expect(Qs.parse('a=b&a[1]=c')).to.deep.equal({ a: ['b', 'c'] }); | ||
done(); | ||
t.test('limits specific array indices to arrayLimit', function (st) { | ||
st.deepEqual(qs.parse('a[20]=a', { arrayLimit: 20 }), { a: ['a'] }); | ||
st.deepEqual(qs.parse('a[21]=a', { arrayLimit: 20 }), { a: { 21: 'a' } }); | ||
st.end(); | ||
}); | ||
it('parses a nested array', function (done) { | ||
t.deepEqual(qs.parse('a[12b]=c'), { a: { '12b': 'c' } }, 'supports keys that begin with a number'); | ||
expect(Qs.parse('a[b][]=c&a[b][]=d')).to.deep.equal({ a: { b: ['c', 'd'] } }); | ||
expect(Qs.parse('a[>=]=25')).to.deep.equal({ a: { '>=': '25' } }); | ||
done(); | ||
t.test('supports encoded = signs', function (st) { | ||
st.deepEqual(qs.parse('he%3Dllo=th%3Dere'), { 'he=llo': 'th=ere' }); | ||
st.end(); | ||
}); | ||
it('allows to specify array indices', function (done) { | ||
expect(Qs.parse('a[1]=c&a[0]=b&a[2]=d')).to.deep.equal({ a: ['b', 'c', 'd'] }); | ||
expect(Qs.parse('a[1]=c&a[0]=b')).to.deep.equal({ a: ['b', 'c'] }); | ||
expect(Qs.parse('a[1]=c')).to.deep.equal({ a: ['c'] }); | ||
done(); | ||
t.test('is ok with url encoded strings', function (st) { | ||
st.deepEqual(qs.parse('a[b%20c]=d'), { a: { 'b c': 'd' } }); | ||
st.deepEqual(qs.parse('a[b]=c%20d'), { a: { b: 'c d' } }); | ||
st.end(); | ||
}); | ||
it('limits specific array indices to 20', function (done) { | ||
expect(Qs.parse('a[20]=a')).to.deep.equal({ a: ['a'] }); | ||
expect(Qs.parse('a[21]=a')).to.deep.equal({ a: { '21': 'a' } }); | ||
done(); | ||
t.test('allows brackets in the value', function (st) { | ||
st.deepEqual(qs.parse('pets=["tobi"]'), { pets: '["tobi"]' }); | ||
st.deepEqual(qs.parse('operators=[">=", "<="]'), { operators: '[">=", "<="]' }); | ||
st.end(); | ||
}); | ||
it('supports keys that begin with a number', function (done) { | ||
expect(Qs.parse('a[12b]=c')).to.deep.equal({ a: { '12b': 'c' } }); | ||
done(); | ||
t.test('allows empty values', function (st) { | ||
st.deepEqual(qs.parse(''), {}); | ||
st.deepEqual(qs.parse(null), {}); | ||
st.deepEqual(qs.parse(undefined), {}); | ||
st.end(); | ||
}); | ||
it('supports encoded = signs', function (done) { | ||
t.test('transforms arrays to objects', function (st) { | ||
st.deepEqual(qs.parse('foo[0]=bar&foo[bad]=baz'), { foo: { 0: 'bar', bad: 'baz' } }); | ||
st.deepEqual(qs.parse('foo[bad]=baz&foo[0]=bar'), { foo: { bad: 'baz', 0: 'bar' } }); | ||
st.deepEqual(qs.parse('foo[bad]=baz&foo[]=bar'), { foo: { bad: 'baz', 0: 'bar' } }); | ||
st.deepEqual(qs.parse('foo[]=bar&foo[bad]=baz'), { foo: { 0: 'bar', bad: 'baz' } }); | ||
st.deepEqual(qs.parse('foo[bad]=baz&foo[]=bar&foo[]=foo'), { foo: { bad: 'baz', 0: 'bar', 1: 'foo' } }); | ||
st.deepEqual(qs.parse('foo[0][a]=a&foo[0][b]=b&foo[1][a]=aa&foo[1][b]=bb'), { foo: [{ a: 'a', b: 'b' }, { a: 'aa', b: 'bb' }] }); | ||
expect(Qs.parse('he%3Dllo=th%3Dere')).to.deep.equal({ 'he=llo': 'th=ere' }); | ||
done(); | ||
st.deepEqual(qs.parse('a[]=b&a[t]=u&a[hasOwnProperty]=c', { allowPrototypes: false }), { a: { 0: 'b', t: 'u' } }); | ||
st.deepEqual(qs.parse('a[]=b&a[t]=u&a[hasOwnProperty]=c', { allowPrototypes: true }), { a: { 0: 'b', t: 'u', hasOwnProperty: 'c' } }); | ||
st.deepEqual(qs.parse('a[]=b&a[hasOwnProperty]=c&a[x]=y', { allowPrototypes: false }), { a: { 0: 'b', x: 'y' } }); | ||
st.deepEqual(qs.parse('a[]=b&a[hasOwnProperty]=c&a[x]=y', { allowPrototypes: true }), { a: { 0: 'b', hasOwnProperty: 'c', x: 'y' } }); | ||
st.end(); | ||
}); | ||
it('is ok with url encoded strings', function (done) { | ||
expect(Qs.parse('a[b%20c]=d')).to.deep.equal({ a: { 'b c': 'd' } }); | ||
expect(Qs.parse('a[b]=c%20d')).to.deep.equal({ a: { b: 'c d' } }); | ||
done(); | ||
t.test('transforms arrays to objects (dot notation)', function (st) { | ||
st.deepEqual(qs.parse('foo[0].baz=bar&fool.bad=baz', { allowDots: true }), { foo: [{ baz: 'bar' }], fool: { bad: 'baz' } }); | ||
st.deepEqual(qs.parse('foo[0].baz=bar&fool.bad.boo=baz', { allowDots: true }), { foo: [{ baz: 'bar' }], fool: { bad: { boo: 'baz' } } }); | ||
st.deepEqual(qs.parse('foo[0][0].baz=bar&fool.bad=baz', { allowDots: true }), { foo: [[{ baz: 'bar' }]], fool: { bad: 'baz' } }); | ||
st.deepEqual(qs.parse('foo[0].baz[0]=15&foo[0].bar=2', { allowDots: true }), { foo: [{ baz: ['15'], bar: '2' }] }); | ||
st.deepEqual(qs.parse('foo[0].baz[0]=15&foo[0].baz[1]=16&foo[0].bar=2', { allowDots: true }), { foo: [{ baz: ['15', '16'], bar: '2' }] }); | ||
st.deepEqual(qs.parse('foo.bad=baz&foo[0]=bar', { allowDots: true }), { foo: { bad: 'baz', 0: 'bar' } }); | ||
st.deepEqual(qs.parse('foo.bad=baz&foo[]=bar', { allowDots: true }), { foo: { bad: 'baz', 0: 'bar' } }); | ||
st.deepEqual(qs.parse('foo[]=bar&foo.bad=baz', { allowDots: true }), { foo: { 0: 'bar', bad: 'baz' } }); | ||
st.deepEqual(qs.parse('foo.bad=baz&foo[]=bar&foo[]=foo', { allowDots: true }), { foo: { bad: 'baz', 0: 'bar', 1: 'foo' } }); | ||
st.deepEqual(qs.parse('foo[0].a=a&foo[0].b=b&foo[1].a=aa&foo[1].b=bb', { allowDots: true }), { foo: [{ a: 'a', b: 'b' }, { a: 'aa', b: 'bb' }] }); | ||
st.end(); | ||
}); | ||
it('allows brackets in the value', function (done) { | ||
expect(Qs.parse('pets=["tobi"]')).to.deep.equal({ pets: '["tobi"]' }); | ||
expect(Qs.parse('operators=[">=", "<="]')).to.deep.equal({ operators: '[">=", "<="]' }); | ||
done(); | ||
t.test('correctly prunes undefined values when converting an array to an object', function (st) { | ||
st.deepEqual(qs.parse('a[2]=b&a[99999999]=c'), { a: { 2: 'b', 99999999: 'c' } }); | ||
st.end(); | ||
}); | ||
it('allows empty values', function (done) { | ||
expect(Qs.parse('')).to.deep.equal({}); | ||
expect(Qs.parse(null)).to.deep.equal({}); | ||
expect(Qs.parse(undefined)).to.deep.equal({}); | ||
done(); | ||
t.test('supports malformed uri characters', function (st) { | ||
st.deepEqual(qs.parse('{%:%}', { strictNullHandling: true }), { '{%:%}': null }); | ||
st.deepEqual(qs.parse('{%:%}='), { '{%:%}': '' }); | ||
st.deepEqual(qs.parse('foo=%:%}'), { foo: '%:%}' }); | ||
st.end(); | ||
}); | ||
it('transforms arrays to objects', function (done) { | ||
expect(Qs.parse('foo[0]=bar&foo[bad]=baz')).to.deep.equal({ foo: { '0': 'bar', bad: 'baz' } }); | ||
expect(Qs.parse('foo[bad]=baz&foo[0]=bar')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar' } }); | ||
expect(Qs.parse('foo[bad]=baz&foo[]=bar')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar' } }); | ||
expect(Qs.parse('foo[]=bar&foo[bad]=baz')).to.deep.equal({ foo: { '0': 'bar', bad: 'baz' } }); | ||
expect(Qs.parse('foo[bad]=baz&foo[]=bar&foo[]=foo')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar', '1': 'foo' } }); | ||
expect(Qs.parse('foo[0][a]=a&foo[0][b]=b&foo[1][a]=aa&foo[1][b]=bb')).to.deep.equal({ foo: [{ a: 'a', b: 'b' }, { a: 'aa', b: 'bb' }] }); | ||
expect(Qs.parse('a[]=b&a[t]=u&a[hasOwnProperty]=c')).to.deep.equal({ a: { '0': 'b', t: 'u', c: true } }); | ||
expect(Qs.parse('a[]=b&a[hasOwnProperty]=c&a[x]=y')).to.deep.equal({ a: { '0': 'b', '1': 'c', x: 'y' } }); | ||
done(); | ||
t.test('doesn\'t produce empty keys', function (st) { | ||
st.deepEqual(qs.parse('_r=1&'), { _r: '1' }); | ||
st.end(); | ||
}); | ||
it('transforms arrays to objects (dot notation)', function (done) { | ||
expect(Qs.parse('foo[0].baz=bar&fool.bad=baz', { allowDots: true })).to.deep.equal({ foo: [{ baz: 'bar' }], fool: { bad: 'baz' } }); | ||
expect(Qs.parse('foo[0].baz=bar&fool.bad.boo=baz', { allowDots: true })).to.deep.equal({ foo: [{ baz: 'bar' }], fool: { bad: { boo: 'baz' } } }); | ||
expect(Qs.parse('foo[0][0].baz=bar&fool.bad=baz', { allowDots: true })).to.deep.equal({ foo: [[{ baz: 'bar' }]], fool: { bad: 'baz' } }); | ||
expect(Qs.parse('foo[0].baz[0]=15&foo[0].bar=2', { allowDots: true })).to.deep.equal({ foo: [{ baz: ['15'], bar: '2' }] }); | ||
expect(Qs.parse('foo[0].baz[0]=15&foo[0].baz[1]=16&foo[0].bar=2', { allowDots: true })).to.deep.equal({ foo: [{ baz: ['15', '16'], bar: '2' }] }); | ||
expect(Qs.parse('foo.bad=baz&foo[0]=bar', { allowDots: true })).to.deep.equal({ foo: { bad: 'baz', '0': 'bar' } }); | ||
expect(Qs.parse('foo.bad=baz&foo[]=bar', { allowDots: true })).to.deep.equal({ foo: { bad: 'baz', '0': 'bar' } }); | ||
expect(Qs.parse('foo[]=bar&foo.bad=baz', { allowDots: true })).to.deep.equal({ foo: { '0': 'bar', bad: 'baz' } }); | ||
expect(Qs.parse('foo.bad=baz&foo[]=bar&foo[]=foo', { allowDots: true })).to.deep.equal({ foo: { bad: 'baz', '0': 'bar', '1': 'foo' } }); | ||
expect(Qs.parse('foo[0].a=a&foo[0].b=b&foo[1].a=aa&foo[1].b=bb', { allowDots: true })).to.deep.equal({ foo: [{ a: 'a', b: 'b' }, { a: 'aa', b: 'bb' }] }); | ||
done(); | ||
t.test('cannot access Object prototype', function (st) { | ||
qs.parse('constructor[prototype][bad]=bad'); | ||
qs.parse('bad[constructor][prototype][bad]=bad'); | ||
st.equal(typeof Object.prototype.bad, 'undefined'); | ||
st.end(); | ||
}); | ||
it('can add keys to objects', function (done) { | ||
expect(Qs.parse('a[b]=c&a=d')).to.deep.equal({ a: { b: 'c', d: true } }); | ||
done(); | ||
t.test('parses arrays of objects', function (st) { | ||
st.deepEqual(qs.parse('a[][b]=c'), { a: [{ b: 'c' }] }); | ||
st.deepEqual(qs.parse('a[0][b]=c'), { a: [{ b: 'c' }] }); | ||
st.end(); | ||
}); | ||
it('correctly prunes undefined values when converting an array to an object', function (done) { | ||
t.test('allows for empty strings in arrays', function (st) { | ||
st.deepEqual(qs.parse('a[]=b&a[]=&a[]=c'), { a: ['b', '', 'c'] }); | ||
expect(Qs.parse('a[2]=b&a[99999999]=c')).to.deep.equal({ a: { '2': 'b', '99999999': 'c' } }); | ||
done(); | ||
}); | ||
st.deepEqual( | ||
qs.parse('a[0]=b&a[1]&a[2]=c&a[19]=', { strictNullHandling: true, arrayLimit: 20 }), | ||
{ a: ['b', null, 'c', ''] }, | ||
'with arrayLimit 20 + array indices: null then empty string works' | ||
); | ||
st.deepEqual( | ||
qs.parse('a[]=b&a[]&a[]=c&a[]=', { strictNullHandling: true, arrayLimit: 0 }), | ||
{ a: ['b', null, 'c', ''] }, | ||
'with arrayLimit 0 + array brackets: null then empty string works' | ||
); | ||
it('supports malformed uri characters', function (done) { | ||
st.deepEqual( | ||
qs.parse('a[0]=b&a[1]=&a[2]=c&a[19]', { strictNullHandling: true, arrayLimit: 20 }), | ||
{ a: ['b', '', 'c', null] }, | ||
'with arrayLimit 20 + array indices: empty string then null works' | ||
); | ||
st.deepEqual( | ||
qs.parse('a[]=b&a[]=&a[]=c&a[]', { strictNullHandling: true, arrayLimit: 0 }), | ||
{ a: ['b', '', 'c', null] }, | ||
'with arrayLimit 0 + array brackets: empty string then null works' | ||
); | ||
expect(Qs.parse('{%:%}', { strictNullHandling: true })).to.deep.equal({ '{%:%}': null }); | ||
expect(Qs.parse('{%:%}=')).to.deep.equal({ '{%:%}': '' }); | ||
expect(Qs.parse('foo=%:%}')).to.deep.equal({ foo: '%:%}' }); | ||
done(); | ||
st.deepEqual( | ||
qs.parse('a[]=&a[]=b&a[]=c'), | ||
{ a: ['', 'b', 'c'] }, | ||
'array brackets: empty strings work' | ||
); | ||
st.end(); | ||
}); | ||
it('doesn\'t produce empty keys', function (done) { | ||
expect(Qs.parse('_r=1&')).to.deep.equal({ '_r': '1' }); | ||
done(); | ||
t.test('compacts sparse arrays', function (st) { | ||
st.deepEqual(qs.parse('a[10]=1&a[2]=2', { arrayLimit: 20 }), { a: ['2', '1'] }); | ||
st.deepEqual(qs.parse('a[1][b][2][c]=1', { arrayLimit: 20 }), { a: [{ b: [{ c: '1' }] }] }); | ||
st.deepEqual(qs.parse('a[1][2][3][c]=1', { arrayLimit: 20 }), { a: [[[{ c: '1' }]]] }); | ||
st.deepEqual(qs.parse('a[1][2][3][c][1]=1', { arrayLimit: 20 }), { a: [[[{ c: ['1'] }]]] }); | ||
st.end(); | ||
}); | ||
it('cannot access Object prototype', function (done) { | ||
Qs.parse('constructor[prototype][bad]=bad'); | ||
Qs.parse('bad[constructor][prototype][bad]=bad'); | ||
expect(typeof Object.prototype.bad).to.equal('undefined'); | ||
done(); | ||
t.test('parses semi-parsed strings', function (st) { | ||
st.deepEqual(qs.parse({ 'a[b]': 'c' }), { a: { b: 'c' } }); | ||
st.deepEqual(qs.parse({ 'a[b]': 'c', 'a[d]': 'e' }), { a: { b: 'c', d: 'e' } }); | ||
st.end(); | ||
}); | ||
it('parses arrays of objects', function (done) { | ||
expect(Qs.parse('a[][b]=c')).to.deep.equal({ a: [{ b: 'c' }] }); | ||
expect(Qs.parse('a[0][b]=c')).to.deep.equal({ a: [{ b: 'c' }] }); | ||
done(); | ||
t.test('parses buffers correctly', function (st) { | ||
var b = SaferBuffer.from('test'); | ||
st.deepEqual(qs.parse({ a: b }), { a: b }); | ||
st.end(); | ||
}); | ||
it('allows for empty strings in arrays', function (done) { | ||
expect(Qs.parse('a[]=b&a[]=&a[]=c')).to.deep.equal({ a: ['b', '', 'c'] }); | ||
expect(Qs.parse('a[0]=b&a[1]&a[2]=c&a[19]=', { strictNullHandling: true, arrayLimit: 20 })).to.deep.equal({ a: ['b', null, 'c', ''] }); | ||
expect(Qs.parse('a[]=b&a[]&a[]=c&a[]=', { strictNullHandling: true, arrayLimit: 0 })).to.deep.equal({ a: ['b', null, 'c', ''] }); | ||
expect(Qs.parse('a[0]=b&a[1]=&a[2]=c&a[19]', { strictNullHandling: true, arrayLimit: 20 })).to.deep.equal({ a: ['b', '', 'c', null] }); | ||
expect(Qs.parse('a[]=b&a[]=&a[]=c&a[]', { strictNullHandling: true, arrayLimit: 0 })).to.deep.equal({ a: ['b', '', 'c', null] }); | ||
expect(Qs.parse('a[]=&a[]=b&a[]=c')).to.deep.equal({ a: ['', 'b', 'c'] }); | ||
done(); | ||
t.test('continues parsing when no parent is found', function (st) { | ||
st.deepEqual(qs.parse('[]=&a=b'), { 0: '', a: 'b' }); | ||
st.deepEqual(qs.parse('[]&a=b', { strictNullHandling: true }), { 0: null, a: 'b' }); | ||
st.deepEqual(qs.parse('[foo]=bar'), { foo: 'bar' }); | ||
st.end(); | ||
}); | ||
it('compacts sparse arrays', function (done) { | ||
expect(Qs.parse('a[10]=1&a[2]=2')).to.deep.equal({ a: ['2', '1'] }); | ||
done(); | ||
}); | ||
it('parses semi-parsed strings', function (done) { | ||
expect(Qs.parse({ 'a[b]': 'c' })).to.deep.equal({ a: { b: 'c' } }); | ||
expect(Qs.parse({ 'a[b]': 'c', 'a[d]': 'e' })).to.deep.equal({ a: { b: 'c', d: 'e' } }); | ||
done(); | ||
}); | ||
it('parses buffers correctly', function (done) { | ||
var b = new Buffer('test'); | ||
expect(Qs.parse({ a: b })).to.deep.equal({ a: b }); | ||
done(); | ||
}); | ||
it('continues parsing when no parent is found', function (done) { | ||
expect(Qs.parse('[]=&a=b')).to.deep.equal({ '0': '', a: 'b' }); | ||
expect(Qs.parse('[]&a=b', { strictNullHandling: true })).to.deep.equal({ '0': null, a: 'b' }); | ||
expect(Qs.parse('[foo]=bar')).to.deep.equal({ foo: 'bar' }); | ||
done(); | ||
}); | ||
it('does not error when parsing a very long array', function (done) { | ||
t.test('does not error when parsing a very long array', function (st) { | ||
var str = 'a[]=a'; | ||
while (Buffer.byteLength(str) < 128 * 1024) { | ||
str += '&' + str; | ||
str = str + '&' + str; | ||
} | ||
expect(function () { | ||
st.doesNotThrow(function () { | ||
qs.parse(str); | ||
}); | ||
Qs.parse(str); | ||
}).to.not.throw(); | ||
done(); | ||
st.end(); | ||
}); | ||
it('should not throw when a native prototype has an enumerable property', { parallel: false }, function (done) { | ||
t.test('should not throw when a native prototype has an enumerable property', { parallel: false }, function (st) { | ||
Object.prototype.crash = ''; | ||
Array.prototype.crash = ''; | ||
expect(Qs.parse.bind(null, 'a=b')).to.not.throw(); | ||
expect(Qs.parse('a=b')).to.deep.equal({ a: 'b' }); | ||
expect(Qs.parse.bind(null, 'a[][b]=c')).to.not.throw(); | ||
expect(Qs.parse('a[][b]=c')).to.deep.equal({ a: [{ b: 'c' }] }); | ||
st.doesNotThrow(qs.parse.bind(null, 'a=b')); | ||
st.deepEqual(qs.parse('a=b'), { a: 'b' }); | ||
st.doesNotThrow(qs.parse.bind(null, 'a[][b]=c')); | ||
st.deepEqual(qs.parse('a[][b]=c'), { a: [{ b: 'c' }] }); | ||
delete Object.prototype.crash; | ||
delete Array.prototype.crash; | ||
done(); | ||
st.end(); | ||
}); | ||
it('parses a string with an alternative string delimiter', function (done) { | ||
t.test('parses a string with an alternative string delimiter', function (st) { | ||
st.deepEqual(qs.parse('a=b;c=d', { delimiter: ';' }), { a: 'b', c: 'd' }); | ||
st.end(); | ||
}); | ||
expect(Qs.parse('a=b;c=d', { delimiter: ';' })).to.deep.equal({ a: 'b', c: 'd' }); | ||
done(); | ||
t.test('parses a string with an alternative RegExp delimiter', function (st) { | ||
st.deepEqual(qs.parse('a=b; c=d', { delimiter: /[;,] */ }), { a: 'b', c: 'd' }); | ||
st.end(); | ||
}); | ||
it('parses a string with an alternative RegExp delimiter', function (done) { | ||
expect(Qs.parse('a=b; c=d', { delimiter: /[;,] */ })).to.deep.equal({ a: 'b', c: 'd' }); | ||
done(); | ||
t.test('does not use non-splittable objects as delimiters', function (st) { | ||
st.deepEqual(qs.parse('a=b&c=d', { delimiter: true }), { a: 'b', c: 'd' }); | ||
st.end(); | ||
}); | ||
it('does not use non-splittable objects as delimiters', function (done) { | ||
expect(Qs.parse('a=b&c=d', { delimiter: true })).to.deep.equal({ a: 'b', c: 'd' }); | ||
done(); | ||
t.test('allows overriding parameter limit', function (st) { | ||
st.deepEqual(qs.parse('a=b&c=d', { parameterLimit: 1 }), { a: 'b' }); | ||
st.end(); | ||
}); | ||
it('allows overriding parameter limit', function (done) { | ||
expect(Qs.parse('a=b&c=d', { parameterLimit: 1 })).to.deep.equal({ a: 'b' }); | ||
done(); | ||
t.test('allows setting the parameter limit to Infinity', function (st) { | ||
st.deepEqual(qs.parse('a=b&c=d', { parameterLimit: Infinity }), { a: 'b', c: 'd' }); | ||
st.end(); | ||
}); | ||
it('allows setting the parameter limit to Infinity', function (done) { | ||
expect(Qs.parse('a=b&c=d', { parameterLimit: Infinity })).to.deep.equal({ a: 'b', c: 'd' }); | ||
done(); | ||
t.test('allows overriding array limit', function (st) { | ||
st.deepEqual(qs.parse('a[0]=b', { arrayLimit: -1 }), { a: { 0: 'b' } }); | ||
st.deepEqual(qs.parse('a[-1]=b', { arrayLimit: -1 }), { a: { '-1': 'b' } }); | ||
st.deepEqual(qs.parse('a[0]=b&a[1]=c', { arrayLimit: 0 }), { a: { 0: 'b', 1: 'c' } }); | ||
st.end(); | ||
}); | ||
it('allows overriding array limit', function (done) { | ||
expect(Qs.parse('a[0]=b', { arrayLimit: -1 })).to.deep.equal({ a: { '0': 'b' } }); | ||
expect(Qs.parse('a[-1]=b', { arrayLimit: -1 })).to.deep.equal({ a: { '-1': 'b' } }); | ||
expect(Qs.parse('a[0]=b&a[1]=c', { arrayLimit: 0 })).to.deep.equal({ a: { '0': 'b', '1': 'c' } }); | ||
done(); | ||
t.test('allows disabling array parsing', function (st) { | ||
st.deepEqual(qs.parse('a[0]=b&a[1]=c', { parseArrays: false }), { a: { 0: 'b', 1: 'c' } }); | ||
st.end(); | ||
}); | ||
it('allows disabling array parsing', function (done) { | ||
expect(Qs.parse('a[0]=b&a[1]=c', { parseArrays: false })).to.deep.equal({ a: { '0': 'b', '1': 'c' } }); | ||
done(); | ||
t.test('allows for query string prefix', function (st) { | ||
st.deepEqual(qs.parse('?foo=bar', { ignoreQueryPrefix: true }), { foo: 'bar' }); | ||
st.deepEqual(qs.parse('foo=bar', { ignoreQueryPrefix: true }), { foo: 'bar' }); | ||
st.deepEqual(qs.parse('?foo=bar', { ignoreQueryPrefix: false }), { '?foo': 'bar' }); | ||
st.end(); | ||
}); | ||
it('parses an object', function (done) { | ||
t.test('parses an object', function (st) { | ||
var input = { | ||
@@ -355,16 +324,15 @@ 'user[name]': { 'pop[bob]': 3 }, | ||
var expected = { | ||
'user': { | ||
'name': { 'pop[bob]': 3 }, | ||
'email': null | ||
user: { | ||
name: { 'pop[bob]': 3 }, | ||
email: null | ||
} | ||
}; | ||
var result = Qs.parse(input); | ||
var result = qs.parse(input); | ||
expect(result).to.deep.equal(expected); | ||
done(); | ||
st.deepEqual(result, expected); | ||
st.end(); | ||
}); | ||
it('parses an object in dot notation', function (done) { | ||
t.test('parses an object in dot notation', function (st) { | ||
var input = { | ||
@@ -376,18 +344,17 @@ 'user.name': { 'pop[bob]': 3 }, | ||
var expected = { | ||
'user': { | ||
'name': { 'pop[bob]': 3 }, | ||
'email': null | ||
user: { | ||
name: { 'pop[bob]': 3 }, | ||
email: null | ||
} | ||
}; | ||
var result = Qs.parse(input, { allowDots: true }); | ||
var result = qs.parse(input, { allowDots: true }); | ||
expect(result).to.deep.equal(expected); | ||
done(); | ||
st.deepEqual(result, expected); | ||
st.end(); | ||
}); | ||
it('parses an object and not child values', function (done) { | ||
t.test('parses an object and not child values', function (st) { | ||
var input = { | ||
'user[name]': { 'pop[bob]': { 'test': 3 } }, | ||
'user[name]': { 'pop[bob]': { test: 3 } }, | ||
'user[email]': null | ||
@@ -397,26 +364,24 @@ }; | ||
var expected = { | ||
'user': { | ||
'name': { 'pop[bob]': { 'test': 3 } }, | ||
'email': null | ||
user: { | ||
name: { 'pop[bob]': { test: 3 } }, | ||
email: null | ||
} | ||
}; | ||
var result = Qs.parse(input); | ||
var result = qs.parse(input); | ||
expect(result).to.deep.equal(expected); | ||
done(); | ||
st.deepEqual(result, expected); | ||
st.end(); | ||
}); | ||
it('does not blow up when Buffer global is missing', function (done) { | ||
t.test('does not blow up when Buffer global is missing', function (st) { | ||
var tempBuffer = global.Buffer; | ||
delete global.Buffer; | ||
var result = Qs.parse('a=b&c=d'); | ||
var result = qs.parse('a=b&c=d'); | ||
global.Buffer = tempBuffer; | ||
expect(result).to.deep.equal({ a: 'b', c: 'd' }); | ||
done(); | ||
st.deepEqual(result, { a: 'b', c: 'd' }); | ||
st.end(); | ||
}); | ||
it('does not crash when parsing circular references', function (done) { | ||
t.test('does not crash when parsing circular references', function (st) { | ||
var a = {}; | ||
@@ -427,49 +392,133 @@ a.b = a; | ||
expect(function () { | ||
st.doesNotThrow(function () { | ||
parsed = qs.parse({ 'foo[bar]': 'baz', 'foo[baz]': a }); | ||
}); | ||
parsed = Qs.parse({ 'foo[bar]': 'baz', 'foo[baz]': a }); | ||
}).to.not.throw(); | ||
st.equal('foo' in parsed, true, 'parsed has "foo" property'); | ||
st.equal('bar' in parsed.foo, true); | ||
st.equal('baz' in parsed.foo, true); | ||
st.equal(parsed.foo.bar, 'baz'); | ||
st.deepEqual(parsed.foo.baz, a); | ||
st.end(); | ||
}); | ||
expect(parsed).to.contain('foo'); | ||
expect(parsed.foo).to.contain('bar', 'baz'); | ||
expect(parsed.foo.bar).to.equal('baz'); | ||
expect(parsed.foo.baz).to.deep.equal(a); | ||
done(); | ||
t.test('does not crash when parsing deep objects', function (st) { | ||
var parsed; | ||
var str = 'foo'; | ||
for (var i = 0; i < 5000; i++) { | ||
str += '[p]'; | ||
} | ||
str += '=bar'; | ||
st.doesNotThrow(function () { | ||
parsed = qs.parse(str, { depth: 5000 }); | ||
}); | ||
st.equal('foo' in parsed, true, 'parsed has "foo" property'); | ||
var depth = 0; | ||
var ref = parsed.foo; | ||
while ((ref = ref.p)) { | ||
depth += 1; | ||
} | ||
st.equal(depth, 5000, 'parsed is 5000 properties deep'); | ||
st.end(); | ||
}); | ||
it('parses plain objects correctly', function (done) { | ||
t.test('parses null objects correctly', { skip: !Object.create }, function (st) { | ||
var a = Object.create(null); | ||
a.b = 'c'; | ||
expect(Qs.parse(a)).to.deep.equal({ b: 'c' }); | ||
var result = Qs.parse({ a: a }); | ||
expect(result).to.contain('a'); | ||
expect(result.a).to.deep.equal(a); | ||
done(); | ||
st.deepEqual(qs.parse(a), { b: 'c' }); | ||
var result = qs.parse({ a: a }); | ||
st.equal('a' in result, true, 'result has "a" property'); | ||
st.deepEqual(result.a, a); | ||
st.end(); | ||
}); | ||
it('parses dates correctly', function (done) { | ||
t.test('parses dates correctly', function (st) { | ||
var now = new Date(); | ||
expect(Qs.parse({ a: now })).to.deep.equal({ a: now }); | ||
done(); | ||
st.deepEqual(qs.parse({ a: now }), { a: now }); | ||
st.end(); | ||
}); | ||
it('parses regular expressions correctly', function (done) { | ||
t.test('parses regular expressions correctly', function (st) { | ||
var re = /^test$/; | ||
expect(Qs.parse({ a: re })).to.deep.equal({ a: re }); | ||
done(); | ||
st.deepEqual(qs.parse({ a: re }), { a: re }); | ||
st.end(); | ||
}); | ||
it('can allow overwriting prototype properties', function (done) { | ||
t.test('does not allow overwriting prototype properties', function (st) { | ||
st.deepEqual(qs.parse('a[hasOwnProperty]=b', { allowPrototypes: false }), {}); | ||
st.deepEqual(qs.parse('hasOwnProperty=b', { allowPrototypes: false }), {}); | ||
expect(Qs.parse('a[hasOwnProperty]=b', { allowPrototypes: true })).to.deep.equal({ a: { hasOwnProperty: 'b' } }, { prototype: false }); | ||
expect(Qs.parse('hasOwnProperty=b', { allowPrototypes: true })).to.deep.equal({ hasOwnProperty: 'b' }, { prototype: false }); | ||
done(); | ||
st.deepEqual( | ||
qs.parse('toString', { allowPrototypes: false }), | ||
{}, | ||
'bare "toString" results in {}' | ||
); | ||
st.end(); | ||
}); | ||
it('can return plain objects', function (done) { | ||
t.test('can allow overwriting prototype properties', function (st) { | ||
st.deepEqual(qs.parse('a[hasOwnProperty]=b', { allowPrototypes: true }), { a: { hasOwnProperty: 'b' } }); | ||
st.deepEqual(qs.parse('hasOwnProperty=b', { allowPrototypes: true }), { hasOwnProperty: 'b' }); | ||
st.deepEqual( | ||
qs.parse('toString', { allowPrototypes: true }), | ||
{ toString: '' }, | ||
'bare "toString" results in { toString: "" }' | ||
); | ||
st.end(); | ||
}); | ||
t.test('params starting with a closing bracket', function (st) { | ||
st.deepEqual(qs.parse(']=toString'), { ']': 'toString' }); | ||
st.deepEqual(qs.parse(']]=toString'), { ']]': 'toString' }); | ||
st.deepEqual(qs.parse(']hello]=toString'), { ']hello]': 'toString' }); | ||
st.end(); | ||
}); | ||
t.test('params starting with a starting bracket', function (st) { | ||
st.deepEqual(qs.parse('[=toString'), { '[': 'toString' }); | ||
st.deepEqual(qs.parse('[[=toString'), { '[[': 'toString' }); | ||
st.deepEqual(qs.parse('[hello[=toString'), { '[hello[': 'toString' }); | ||
st.end(); | ||
}); | ||
t.test('add keys to objects', function (st) { | ||
st.deepEqual( | ||
qs.parse('a[b]=c&a=d'), | ||
{ a: { b: 'c', d: true } }, | ||
'can add keys to objects' | ||
); | ||
st.deepEqual( | ||
qs.parse('a[b]=c&a=toString'), | ||
{ a: { b: 'c' } }, | ||
'can not overwrite prototype' | ||
); | ||
st.deepEqual( | ||
qs.parse('a[b]=c&a=toString', { allowPrototypes: true }), | ||
{ a: { b: 'c', toString: true } }, | ||
'can overwrite prototype with allowPrototypes true' | ||
); | ||
st.deepEqual( | ||
qs.parse('a[b]=c&a=toString', { plainObjects: true }), | ||
{ a: { b: 'c', toString: true } }, | ||
'can overwrite prototype with plainObjects true' | ||
); | ||
st.end(); | ||
}); | ||
t.test('can return null objects', { skip: !Object.create }, function (st) { | ||
var expected = Object.create(null); | ||
@@ -479,11 +528,53 @@ expected.a = Object.create(null); | ||
expected.a.hasOwnProperty = 'd'; | ||
expect(Qs.parse('a[b]=c&a[hasOwnProperty]=d', { plainObjects: true })).to.deep.equal(expected); | ||
expect(Qs.parse(null, { plainObjects: true })).to.deep.equal(Object.create(null)); | ||
st.deepEqual(qs.parse('a[b]=c&a[hasOwnProperty]=d', { plainObjects: true }), expected); | ||
st.deepEqual(qs.parse(null, { plainObjects: true }), Object.create(null)); | ||
var expectedArray = Object.create(null); | ||
expectedArray.a = Object.create(null); | ||
expectedArray.a['0'] = 'b'; | ||
expectedArray.a[0] = 'b'; | ||
expectedArray.a.c = 'd'; | ||
expect(Qs.parse('a[]=b&a[c]=d', { plainObjects: true })).to.deep.equal(expectedArray); | ||
done(); | ||
st.deepEqual(qs.parse('a[]=b&a[c]=d', { plainObjects: true }), expectedArray); | ||
st.end(); | ||
}); | ||
t.test('can parse with custom encoding', function (st) { | ||
st.deepEqual(qs.parse('%8c%a7=%91%e5%8d%e3%95%7b', { | ||
decoder: function (str) { | ||
var reg = /%([0-9A-F]{2})/ig; | ||
var result = []; | ||
var parts = reg.exec(str); | ||
while (parts) { | ||
result.push(parseInt(parts[1], 16)); | ||
parts = reg.exec(str); | ||
} | ||
return iconv.decode(SaferBuffer.from(result), 'shift_jis').toString(); | ||
} | ||
}), { η: '倧ιͺεΊ' }); | ||
st.end(); | ||
}); | ||
t.test('receives the default decoder as a second argument', function (st) { | ||
st.plan(1); | ||
qs.parse('a', { | ||
decoder: function (str, defaultDecoder) { | ||
st.equal(defaultDecoder, utils.decode); | ||
} | ||
}); | ||
st.end(); | ||
}); | ||
t.test('throws error with wrong decoder', function (st) { | ||
st['throws'](function () { | ||
qs.parse({}, { decoder: 'string' }); | ||
}, new TypeError('Decoder has to be a function.')); | ||
st.end(); | ||
}); | ||
t.test('does not mutate the options argument', function (st) { | ||
var options = {}; | ||
qs.parse('a[b]=true', options); | ||
st.deepEqual(options, {}); | ||
st.end(); | ||
}); | ||
t.end(); | ||
}); |
@@ -1,263 +0,403 @@ | ||
/* eslint no-extend-native:0 */ | ||
// Load modules | ||
'use strict'; | ||
var Code = require('code'); | ||
var Lab = require('lab'); | ||
var Qs = require('../'); | ||
var test = require('tape'); | ||
var qs = require('../'); | ||
var utils = require('../lib/utils'); | ||
var iconv = require('iconv-lite'); | ||
var SaferBuffer = require('safer-buffer').Buffer; | ||
test('stringify()', function (t) { | ||
t.test('stringifies a querystring object', function (st) { | ||
st.equal(qs.stringify({ a: 'b' }), 'a=b'); | ||
st.equal(qs.stringify({ a: 1 }), 'a=1'); | ||
st.equal(qs.stringify({ a: 1, b: 2 }), 'a=1&b=2'); | ||
st.equal(qs.stringify({ a: 'A_Z' }), 'a=A_Z'); | ||
st.equal(qs.stringify({ a: 'β¬' }), 'a=%E2%82%AC'); | ||
st.equal(qs.stringify({ a: 'ξ' }), 'a=%EE%80%80'); | ||
st.equal(qs.stringify({ a: 'Χ' }), 'a=%D7%90'); | ||
st.equal(qs.stringify({ a: 'π·' }), 'a=%F0%90%90%B7'); | ||
st.end(); | ||
}); | ||
// Declare internals | ||
t.test('adds query prefix', function (st) { | ||
st.equal(qs.stringify({ a: 'b' }, { addQueryPrefix: true }), '?a=b'); | ||
st.end(); | ||
}); | ||
var internals = {}; | ||
t.test('with query prefix, outputs blank string given an empty object', function (st) { | ||
st.equal(qs.stringify({}, { addQueryPrefix: true }), ''); | ||
st.end(); | ||
}); | ||
t.test('stringifies a nested object', function (st) { | ||
st.equal(qs.stringify({ a: { b: 'c' } }), 'a%5Bb%5D=c'); | ||
st.equal(qs.stringify({ a: { b: { c: { d: 'e' } } } }), 'a%5Bb%5D%5Bc%5D%5Bd%5D=e'); | ||
st.end(); | ||
}); | ||
// Test shortcuts | ||
t.test('stringifies a nested object with dots notation', function (st) { | ||
st.equal(qs.stringify({ a: { b: 'c' } }, { allowDots: true }), 'a.b=c'); | ||
st.equal(qs.stringify({ a: { b: { c: { d: 'e' } } } }, { allowDots: true }), 'a.b.c.d=e'); | ||
st.end(); | ||
}); | ||
var lab = exports.lab = Lab.script(); | ||
var expect = Code.expect; | ||
var describe = lab.experiment; | ||
var it = lab.test; | ||
t.test('stringifies an array value', function (st) { | ||
st.equal( | ||
qs.stringify({ a: ['b', 'c', 'd'] }, { arrayFormat: 'indices' }), | ||
'a%5B0%5D=b&a%5B1%5D=c&a%5B2%5D=d', | ||
'indices => indices' | ||
); | ||
st.equal( | ||
qs.stringify({ a: ['b', 'c', 'd'] }, { arrayFormat: 'brackets' }), | ||
'a%5B%5D=b&a%5B%5D=c&a%5B%5D=d', | ||
'brackets => brackets' | ||
); | ||
st.equal( | ||
qs.stringify({ a: ['b', 'c', 'd'] }), | ||
'a%5B0%5D=b&a%5B1%5D=c&a%5B2%5D=d', | ||
'default => indices' | ||
); | ||
st.end(); | ||
}); | ||
t.test('omits nulls when asked', function (st) { | ||
st.equal(qs.stringify({ a: 'b', c: null }, { skipNulls: true }), 'a=b'); | ||
st.end(); | ||
}); | ||
describe('stringify()', function () { | ||
t.test('omits nested nulls when asked', function (st) { | ||
st.equal(qs.stringify({ a: { b: 'c', d: null } }, { skipNulls: true }), 'a%5Bb%5D=c'); | ||
st.end(); | ||
}); | ||
it('stringifies a querystring object', function (done) { | ||
expect(Qs.stringify({ a: 'b' })).to.equal('a=b'); | ||
expect(Qs.stringify({ a: 1 })).to.equal('a=1'); | ||
expect(Qs.stringify({ a: 1, b: 2 })).to.equal('a=1&b=2'); | ||
expect(Qs.stringify({ a: 'A_Z' })).to.equal('a=A_Z'); | ||
expect(Qs.stringify({ a: 'β¬' })).to.equal('a=%E2%82%AC'); | ||
expect(Qs.stringify({ a: 'ξ' })).to.equal('a=%EE%80%80'); | ||
expect(Qs.stringify({ a: 'Χ' })).to.equal('a=%D7%90'); | ||
expect(Qs.stringify({ a: 'π·' })).to.equal('a=%F0%90%90%B7'); | ||
done(); | ||
t.test('omits array indices when asked', function (st) { | ||
st.equal(qs.stringify({ a: ['b', 'c', 'd'] }, { indices: false }), 'a=b&a=c&a=d'); | ||
st.end(); | ||
}); | ||
it('stringifies a nested object', function (done) { | ||
expect(Qs.stringify({ a: { b: 'c' } })).to.equal('a%5Bb%5D=c'); | ||
expect(Qs.stringify({ a: { b: { c: { d: 'e' } } } })).to.equal('a%5Bb%5D%5Bc%5D%5Bd%5D=e'); | ||
done(); | ||
t.test('stringifies a nested array value', function (st) { | ||
st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { arrayFormat: 'indices' }), 'a%5Bb%5D%5B0%5D=c&a%5Bb%5D%5B1%5D=d'); | ||
st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { arrayFormat: 'brackets' }), 'a%5Bb%5D%5B%5D=c&a%5Bb%5D%5B%5D=d'); | ||
st.equal(qs.stringify({ a: { b: ['c', 'd'] } }), 'a%5Bb%5D%5B0%5D=c&a%5Bb%5D%5B1%5D=d'); | ||
st.end(); | ||
}); | ||
it('stringifies an array value', function (done) { | ||
expect(Qs.stringify({ a: ['b', 'c', 'd'] })).to.equal('a%5B0%5D=b&a%5B1%5D=c&a%5B2%5D=d'); | ||
done(); | ||
t.test('stringifies a nested array value with dots notation', function (st) { | ||
st.equal( | ||
qs.stringify( | ||
{ a: { b: ['c', 'd'] } }, | ||
{ allowDots: true, encode: false, arrayFormat: 'indices' } | ||
), | ||
'a.b[0]=c&a.b[1]=d', | ||
'indices: stringifies with dots + indices' | ||
); | ||
st.equal( | ||
qs.stringify( | ||
{ a: { b: ['c', 'd'] } }, | ||
{ allowDots: true, encode: false, arrayFormat: 'brackets' } | ||
), | ||
'a.b[]=c&a.b[]=d', | ||
'brackets: stringifies with dots + brackets' | ||
); | ||
st.equal( | ||
qs.stringify( | ||
{ a: { b: ['c', 'd'] } }, | ||
{ allowDots: true, encode: false } | ||
), | ||
'a.b[0]=c&a.b[1]=d', | ||
'default: stringifies with dots + indices' | ||
); | ||
st.end(); | ||
}); | ||
it('omits nulls when asked', function (done) { | ||
t.test('stringifies an object inside an array', function (st) { | ||
st.equal( | ||
qs.stringify({ a: [{ b: 'c' }] }, { arrayFormat: 'indices' }), | ||
'a%5B0%5D%5Bb%5D=c', | ||
'indices => brackets' | ||
); | ||
st.equal( | ||
qs.stringify({ a: [{ b: 'c' }] }, { arrayFormat: 'brackets' }), | ||
'a%5B%5D%5Bb%5D=c', | ||
'brackets => brackets' | ||
); | ||
st.equal( | ||
qs.stringify({ a: [{ b: 'c' }] }), | ||
'a%5B0%5D%5Bb%5D=c', | ||
'default => indices' | ||
); | ||
expect(Qs.stringify({ a: 'b', c: null }, { skipNulls: true })).to.equal('a=b'); | ||
done(); | ||
}); | ||
st.equal( | ||
qs.stringify({ a: [{ b: { c: [1] } }] }, { arrayFormat: 'indices' }), | ||
'a%5B0%5D%5Bb%5D%5Bc%5D%5B0%5D=1', | ||
'indices => indices' | ||
); | ||
st.equal( | ||
qs.stringify({ a: [{ b: { c: [1] } }] }, { arrayFormat: 'brackets' }), | ||
'a%5B%5D%5Bb%5D%5Bc%5D%5B%5D=1', | ||
'brackets => brackets' | ||
); | ||
it('omits nested nulls when asked', function (done) { | ||
st.equal( | ||
qs.stringify({ a: [{ b: { c: [1] } }] }), | ||
'a%5B0%5D%5Bb%5D%5Bc%5D%5B0%5D=1', | ||
'default => indices' | ||
); | ||
expect(Qs.stringify({ a: { b: 'c', d: null } }, { skipNulls: true })).to.equal('a%5Bb%5D=c'); | ||
done(); | ||
st.end(); | ||
}); | ||
it('omits array indices when asked', function (done) { | ||
t.test('stringifies an array with mixed objects and primitives', function (st) { | ||
st.equal( | ||
qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encode: false, arrayFormat: 'indices' }), | ||
'a[0][b]=1&a[1]=2&a[2]=3', | ||
'indices => indices' | ||
); | ||
st.equal( | ||
qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encode: false, arrayFormat: 'brackets' }), | ||
'a[][b]=1&a[]=2&a[]=3', | ||
'brackets => brackets' | ||
); | ||
st.equal( | ||
qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encode: false }), | ||
'a[0][b]=1&a[1]=2&a[2]=3', | ||
'default => indices' | ||
); | ||
expect(Qs.stringify({ a: ['b', 'c', 'd'] }, { indices: false })).to.equal('a=b&a=c&a=d'); | ||
done(); | ||
st.end(); | ||
}); | ||
it('stringifies a nested array value', function (done) { | ||
t.test('stringifies an object inside an array with dots notation', function (st) { | ||
st.equal( | ||
qs.stringify( | ||
{ a: [{ b: 'c' }] }, | ||
{ allowDots: true, encode: false, arrayFormat: 'indices' } | ||
), | ||
'a[0].b=c', | ||
'indices => indices' | ||
); | ||
st.equal( | ||
qs.stringify( | ||
{ a: [{ b: 'c' }] }, | ||
{ allowDots: true, encode: false, arrayFormat: 'brackets' } | ||
), | ||
'a[].b=c', | ||
'brackets => brackets' | ||
); | ||
st.equal( | ||
qs.stringify( | ||
{ a: [{ b: 'c' }] }, | ||
{ allowDots: true, encode: false } | ||
), | ||
'a[0].b=c', | ||
'default => indices' | ||
); | ||
expect(Qs.stringify({ a: { b: ['c', 'd'] } })).to.equal('a%5Bb%5D%5B0%5D=c&a%5Bb%5D%5B1%5D=d'); | ||
done(); | ||
}); | ||
st.equal( | ||
qs.stringify( | ||
{ a: [{ b: { c: [1] } }] }, | ||
{ allowDots: true, encode: false, arrayFormat: 'indices' } | ||
), | ||
'a[0].b.c[0]=1', | ||
'indices => indices' | ||
); | ||
st.equal( | ||
qs.stringify( | ||
{ a: [{ b: { c: [1] } }] }, | ||
{ allowDots: true, encode: false, arrayFormat: 'brackets' } | ||
), | ||
'a[].b.c[]=1', | ||
'brackets => brackets' | ||
); | ||
st.equal( | ||
qs.stringify( | ||
{ a: [{ b: { c: [1] } }] }, | ||
{ allowDots: true, encode: false } | ||
), | ||
'a[0].b.c[0]=1', | ||
'default => indices' | ||
); | ||
it('stringifies an object inside an array', function (done) { | ||
expect(Qs.stringify({ a: [{ b: 'c' }] })).to.equal('a%5B0%5D%5Bb%5D=c'); | ||
expect(Qs.stringify({ a: [{ b: { c: [1] } }] })).to.equal('a%5B0%5D%5Bb%5D%5Bc%5D%5B0%5D=1'); | ||
done(); | ||
st.end(); | ||
}); | ||
it('does not omit object keys when indices = false', function (done) { | ||
expect(Qs.stringify({ a: [{ b: 'c' }] }, { indices: false })).to.equal('a%5Bb%5D=c'); | ||
done(); | ||
t.test('does not omit object keys when indices = false', function (st) { | ||
st.equal(qs.stringify({ a: [{ b: 'c' }] }, { indices: false }), 'a%5Bb%5D=c'); | ||
st.end(); | ||
}); | ||
it('uses indices notation for arrays when indices=true', function (done) { | ||
expect(Qs.stringify({ a: ['b', 'c'] }, { indices: true })).to.equal('a%5B0%5D=b&a%5B1%5D=c'); | ||
done(); | ||
t.test('uses indices notation for arrays when indices=true', function (st) { | ||
st.equal(qs.stringify({ a: ['b', 'c'] }, { indices: true }), 'a%5B0%5D=b&a%5B1%5D=c'); | ||
st.end(); | ||
}); | ||
it('uses indices notation for arrays when no arrayFormat is specified', function (done) { | ||
expect(Qs.stringify({ a: ['b', 'c'] })).to.equal('a%5B0%5D=b&a%5B1%5D=c'); | ||
done(); | ||
t.test('uses indices notation for arrays when no arrayFormat is specified', function (st) { | ||
st.equal(qs.stringify({ a: ['b', 'c'] }), 'a%5B0%5D=b&a%5B1%5D=c'); | ||
st.end(); | ||
}); | ||
it('uses indices notation for arrays when no arrayFormat=indices', function (done) { | ||
expect(Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' })).to.equal('a%5B0%5D=b&a%5B1%5D=c'); | ||
done(); | ||
t.test('uses indices notation for arrays when no arrayFormat=indices', function (st) { | ||
st.equal(qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' }), 'a%5B0%5D=b&a%5B1%5D=c'); | ||
st.end(); | ||
}); | ||
it('uses repeat notation for arrays when no arrayFormat=repeat', function (done) { | ||
expect(Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' })).to.equal('a=b&a=c'); | ||
done(); | ||
t.test('uses repeat notation for arrays when no arrayFormat=repeat', function (st) { | ||
st.equal(qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' }), 'a=b&a=c'); | ||
st.end(); | ||
}); | ||
it('uses brackets notation for arrays when no arrayFormat=brackets', function (done) { | ||
expect(Qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' })).to.equal('a%5B%5D=b&a%5B%5D=c'); | ||
done(); | ||
t.test('uses brackets notation for arrays when no arrayFormat=brackets', function (st) { | ||
st.equal(qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' }), 'a%5B%5D=b&a%5B%5D=c'); | ||
st.end(); | ||
}); | ||
it('stringifies a complicated object', function (done) { | ||
expect(Qs.stringify({ a: { b: 'c', d: 'e' } })).to.equal('a%5Bb%5D=c&a%5Bd%5D=e'); | ||
done(); | ||
t.test('stringifies a complicated object', function (st) { | ||
st.equal(qs.stringify({ a: { b: 'c', d: 'e' } }), 'a%5Bb%5D=c&a%5Bd%5D=e'); | ||
st.end(); | ||
}); | ||
it('stringifies an empty value', function (done) { | ||
t.test('stringifies an empty value', function (st) { | ||
st.equal(qs.stringify({ a: '' }), 'a='); | ||
st.equal(qs.stringify({ a: null }, { strictNullHandling: true }), 'a'); | ||
expect(Qs.stringify({ a: '' })).to.equal('a='); | ||
expect(Qs.stringify({ a: null }, { strictNullHandling: true })).to.equal('a'); | ||
st.equal(qs.stringify({ a: '', b: '' }), 'a=&b='); | ||
st.equal(qs.stringify({ a: null, b: '' }, { strictNullHandling: true }), 'a&b='); | ||
expect(Qs.stringify({ a: '', b: '' })).to.equal('a=&b='); | ||
expect(Qs.stringify({ a: null, b: '' }, { strictNullHandling: true })).to.equal('a&b='); | ||
st.equal(qs.stringify({ a: { b: '' } }), 'a%5Bb%5D='); | ||
st.equal(qs.stringify({ a: { b: null } }, { strictNullHandling: true }), 'a%5Bb%5D'); | ||
st.equal(qs.stringify({ a: { b: null } }, { strictNullHandling: false }), 'a%5Bb%5D='); | ||
expect(Qs.stringify({ a: { b: '' } })).to.equal('a%5Bb%5D='); | ||
expect(Qs.stringify({ a: { b: null } }, { strictNullHandling: true })).to.equal('a%5Bb%5D'); | ||
expect(Qs.stringify({ a: { b: null } }, { strictNullHandling: false })).to.equal('a%5Bb%5D='); | ||
done(); | ||
st.end(); | ||
}); | ||
it('stringifies an empty object', function (done) { | ||
t.test('stringifies a null object', { skip: !Object.create }, function (st) { | ||
var obj = Object.create(null); | ||
obj.a = 'b'; | ||
expect(Qs.stringify(obj)).to.equal('a=b'); | ||
done(); | ||
st.equal(qs.stringify(obj), 'a=b'); | ||
st.end(); | ||
}); | ||
it('returns an empty string for invalid input', function (done) { | ||
expect(Qs.stringify(undefined)).to.equal(''); | ||
expect(Qs.stringify(false)).to.equal(''); | ||
expect(Qs.stringify(null)).to.equal(''); | ||
expect(Qs.stringify('')).to.equal(''); | ||
done(); | ||
t.test('returns an empty string for invalid input', function (st) { | ||
st.equal(qs.stringify(undefined), ''); | ||
st.equal(qs.stringify(false), ''); | ||
st.equal(qs.stringify(null), ''); | ||
st.equal(qs.stringify(''), ''); | ||
st.end(); | ||
}); | ||
it('stringifies an object with an empty object as a child', function (done) { | ||
t.test('stringifies an object with a null object as a child', { skip: !Object.create }, function (st) { | ||
var obj = { a: Object.create(null) }; | ||
var obj = { | ||
a: Object.create(null) | ||
}; | ||
obj.a.b = 'c'; | ||
expect(Qs.stringify(obj)).to.equal('a%5Bb%5D=c'); | ||
done(); | ||
st.equal(qs.stringify(obj), 'a%5Bb%5D=c'); | ||
st.end(); | ||
}); | ||
it('drops keys with a value of undefined', function (done) { | ||
t.test('drops keys with a value of undefined', function (st) { | ||
st.equal(qs.stringify({ a: undefined }), ''); | ||
expect(Qs.stringify({ a: undefined })).to.equal(''); | ||
expect(Qs.stringify({ a: { b: undefined, c: null } }, { strictNullHandling: true })).to.equal('a%5Bc%5D'); | ||
expect(Qs.stringify({ a: { b: undefined, c: null } }, { strictNullHandling: false })).to.equal('a%5Bc%5D='); | ||
expect(Qs.stringify({ a: { b: undefined, c: '' } })).to.equal('a%5Bc%5D='); | ||
done(); | ||
st.equal(qs.stringify({ a: { b: undefined, c: null } }, { strictNullHandling: true }), 'a%5Bc%5D'); | ||
st.equal(qs.stringify({ a: { b: undefined, c: null } }, { strictNullHandling: false }), 'a%5Bc%5D='); | ||
st.equal(qs.stringify({ a: { b: undefined, c: '' } }), 'a%5Bc%5D='); | ||
st.end(); | ||
}); | ||
it('url encodes values', function (done) { | ||
expect(Qs.stringify({ a: 'b c' })).to.equal('a=b%20c'); | ||
done(); | ||
t.test('url encodes values', function (st) { | ||
st.equal(qs.stringify({ a: 'b c' }), 'a=b%20c'); | ||
st.end(); | ||
}); | ||
it('stringifies a date', function (done) { | ||
t.test('stringifies a date', function (st) { | ||
var now = new Date(); | ||
var str = 'a=' + encodeURIComponent(now.toISOString()); | ||
expect(Qs.stringify({ a: now })).to.equal(str); | ||
done(); | ||
st.equal(qs.stringify({ a: now }), str); | ||
st.end(); | ||
}); | ||
it('stringifies the weird object from qs', function (done) { | ||
expect(Qs.stringify({ 'my weird field': '~q1!2"\'w$5&7/z8)?' })).to.equal('my%20weird%20field=~q1%212%22%27w%245%267%2Fz8%29%3F'); | ||
done(); | ||
t.test('stringifies the weird object from qs', function (st) { | ||
st.equal(qs.stringify({ 'my weird field': '~q1!2"\'w$5&7/z8)?' }), 'my%20weird%20field=~q1%212%22%27w%245%267%2Fz8%29%3F'); | ||
st.end(); | ||
}); | ||
it('skips properties that are part of the object prototype', function (done) { | ||
t.test('skips properties that are part of the object prototype', function (st) { | ||
Object.prototype.crash = 'test'; | ||
expect(Qs.stringify({ a: 'b' })).to.equal('a=b'); | ||
expect(Qs.stringify({ a: { b: 'c' } })).to.equal('a%5Bb%5D=c'); | ||
st.equal(qs.stringify({ a: 'b' }), 'a=b'); | ||
st.equal(qs.stringify({ a: { b: 'c' } }), 'a%5Bb%5D=c'); | ||
delete Object.prototype.crash; | ||
done(); | ||
st.end(); | ||
}); | ||
it('stringifies boolean values', function (done) { | ||
expect(Qs.stringify({ a: true })).to.equal('a=true'); | ||
expect(Qs.stringify({ a: { b: true } })).to.equal('a%5Bb%5D=true'); | ||
expect(Qs.stringify({ b: false })).to.equal('b=false'); | ||
expect(Qs.stringify({ b: { c: false } })).to.equal('b%5Bc%5D=false'); | ||
done(); | ||
t.test('stringifies boolean values', function (st) { | ||
st.equal(qs.stringify({ a: true }), 'a=true'); | ||
st.equal(qs.stringify({ a: { b: true } }), 'a%5Bb%5D=true'); | ||
st.equal(qs.stringify({ b: false }), 'b=false'); | ||
st.equal(qs.stringify({ b: { c: false } }), 'b%5Bc%5D=false'); | ||
st.end(); | ||
}); | ||
it('stringifies buffer values', function (done) { | ||
expect(Qs.stringify({ a: new Buffer('test') })).to.equal('a=test'); | ||
expect(Qs.stringify({ a: { b: new Buffer('test') } })).to.equal('a%5Bb%5D=test'); | ||
done(); | ||
t.test('stringifies buffer values', function (st) { | ||
st.equal(qs.stringify({ a: SaferBuffer.from('test') }), 'a=test'); | ||
st.equal(qs.stringify({ a: { b: SaferBuffer.from('test') } }), 'a%5Bb%5D=test'); | ||
st.end(); | ||
}); | ||
it('stringifies an object using an alternative delimiter', function (done) { | ||
expect(Qs.stringify({ a: 'b', c: 'd' }, { delimiter: ';' })).to.equal('a=b;c=d'); | ||
done(); | ||
t.test('stringifies an object using an alternative delimiter', function (st) { | ||
st.equal(qs.stringify({ a: 'b', c: 'd' }, { delimiter: ';' }), 'a=b;c=d'); | ||
st.end(); | ||
}); | ||
it('doesn\'t blow up when Buffer global is missing', function (done) { | ||
t.test('doesn\'t blow up when Buffer global is missing', function (st) { | ||
var tempBuffer = global.Buffer; | ||
delete global.Buffer; | ||
var result = Qs.stringify({ a: 'b', c: 'd' }); | ||
var result = qs.stringify({ a: 'b', c: 'd' }); | ||
global.Buffer = tempBuffer; | ||
expect(result).to.equal('a=b&c=d'); | ||
done(); | ||
st.equal(result, 'a=b&c=d'); | ||
st.end(); | ||
}); | ||
it('selects properties when filter=array', function (done) { | ||
t.test('selects properties when filter=array', function (st) { | ||
st.equal(qs.stringify({ a: 'b' }, { filter: ['a'] }), 'a=b'); | ||
st.equal(qs.stringify({ a: 1 }, { filter: [] }), ''); | ||
expect(Qs.stringify({ a: 'b' }, { filter: ['a'] })).to.equal('a=b'); | ||
expect(Qs.stringify({ a: 1 }, { filter: [] })).to.equal(''); | ||
expect(Qs.stringify({ a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' }, { filter: ['a', 'b', 0, 2] })).to.equal('a%5Bb%5D%5B0%5D=1&a%5Bb%5D%5B2%5D=3'); | ||
done(); | ||
st.equal( | ||
qs.stringify( | ||
{ a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' }, | ||
{ filter: ['a', 'b', 0, 2], arrayFormat: 'indices' } | ||
), | ||
'a%5Bb%5D%5B0%5D=1&a%5Bb%5D%5B2%5D=3', | ||
'indices => indices' | ||
); | ||
st.equal( | ||
qs.stringify( | ||
{ a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' }, | ||
{ filter: ['a', 'b', 0, 2], arrayFormat: 'brackets' } | ||
), | ||
'a%5Bb%5D%5B%5D=1&a%5Bb%5D%5B%5D=3', | ||
'brackets => brackets' | ||
); | ||
st.equal( | ||
qs.stringify( | ||
{ a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' }, | ||
{ filter: ['a', 'b', 0, 2] } | ||
), | ||
'a%5Bb%5D%5B0%5D=1&a%5Bb%5D%5B2%5D=3', | ||
'default => indices' | ||
); | ||
st.end(); | ||
}); | ||
it('supports custom representations when filter=function', function (done) { | ||
t.test('supports custom representations when filter=function', function (st) { | ||
var calls = 0; | ||
var obj = { a: 'b', c: 'd', e: { f: new Date(1257894000000) } }; | ||
var filterFunc = function (prefix, value) { | ||
calls++; | ||
calls += 1; | ||
if (calls === 1) { | ||
expect(prefix).to.be.empty(); | ||
expect(value).to.equal(obj); | ||
} | ||
else if (prefix === 'c') { | ||
return; | ||
} | ||
else if (value instanceof Date) { | ||
expect(prefix).to.equal('e[f]'); | ||
st.equal(prefix, '', 'prefix is empty'); | ||
st.equal(value, obj); | ||
} else if (prefix === 'c') { | ||
return void 0; | ||
} else if (value instanceof Date) { | ||
st.equal(prefix, 'e[f]'); | ||
return value.getTime(); | ||
@@ -268,27 +408,191 @@ } | ||
expect(Qs.stringify(obj, { filter: filterFunc })).to.equal('a=b&e%5Bf%5D=1257894000000'); | ||
expect(calls).to.equal(5); | ||
done(); | ||
st.equal(qs.stringify(obj, { filter: filterFunc }), 'a=b&e%5Bf%5D=1257894000000'); | ||
st.equal(calls, 5); | ||
st.end(); | ||
}); | ||
t.test('can disable uri encoding', function (st) { | ||
st.equal(qs.stringify({ a: 'b' }, { encode: false }), 'a=b'); | ||
st.equal(qs.stringify({ a: { b: 'c' } }, { encode: false }), 'a[b]=c'); | ||
st.equal(qs.stringify({ a: 'b', c: null }, { strictNullHandling: true, encode: false }), 'a=b&c'); | ||
st.end(); | ||
}); | ||
it('can disable uri encoding', function (done) { | ||
t.test('can sort the keys', function (st) { | ||
var sort = function (a, b) { | ||
return a.localeCompare(b); | ||
}; | ||
st.equal(qs.stringify({ a: 'c', z: 'y', b: 'f' }, { sort: sort }), 'a=c&b=f&z=y'); | ||
st.equal(qs.stringify({ a: 'c', z: { j: 'a', i: 'b' }, b: 'f' }, { sort: sort }), 'a=c&b=f&z%5Bi%5D=b&z%5Bj%5D=a'); | ||
st.end(); | ||
}); | ||
expect(Qs.stringify({ a: 'b' }, { encode: false })).to.equal('a=b'); | ||
expect(Qs.stringify({ a: { b: 'c' } }, { encode: false })).to.equal('a[b]=c'); | ||
expect(Qs.stringify({ a: 'b', c: null }, { strictNullHandling: true, encode: false })).to.equal('a=b&c'); | ||
done(); | ||
t.test('can sort the keys at depth 3 or more too', function (st) { | ||
var sort = function (a, b) { | ||
return a.localeCompare(b); | ||
}; | ||
st.equal( | ||
qs.stringify( | ||
{ a: 'a', z: { zj: { zjb: 'zjb', zja: 'zja' }, zi: { zib: 'zib', zia: 'zia' } }, b: 'b' }, | ||
{ sort: sort, encode: false } | ||
), | ||
'a=a&b=b&z[zi][zia]=zia&z[zi][zib]=zib&z[zj][zja]=zja&z[zj][zjb]=zjb' | ||
); | ||
st.equal( | ||
qs.stringify( | ||
{ a: 'a', z: { zj: { zjb: 'zjb', zja: 'zja' }, zi: { zib: 'zib', zia: 'zia' } }, b: 'b' }, | ||
{ sort: null, encode: false } | ||
), | ||
'a=a&z[zj][zjb]=zjb&z[zj][zja]=zja&z[zi][zib]=zib&z[zi][zia]=zia&b=b' | ||
); | ||
st.end(); | ||
}); | ||
it('can sort the keys', function (done) { | ||
t.test('can stringify with custom encoding', function (st) { | ||
st.equal(qs.stringify({ η: '倧ιͺεΊ', '': '' }, { | ||
encoder: function (str) { | ||
if (str.length === 0) { | ||
return ''; | ||
} | ||
var buf = iconv.encode(str, 'shiftjis'); | ||
var result = []; | ||
for (var i = 0; i < buf.length; ++i) { | ||
result.push(buf.readUInt8(i).toString(16)); | ||
} | ||
return '%' + result.join('%'); | ||
} | ||
}), '%8c%a7=%91%e5%8d%e3%95%7b&='); | ||
st.end(); | ||
}); | ||
var sort = function alphabeticalSort (a, b) { | ||
t.test('receives the default encoder as a second argument', function (st) { | ||
st.plan(2); | ||
qs.stringify({ a: 1 }, { | ||
encoder: function (str, defaultEncoder) { | ||
st.equal(defaultEncoder, utils.encode); | ||
} | ||
}); | ||
st.end(); | ||
}); | ||
return a.localeCompare(b); | ||
t.test('throws error with wrong encoder', function (st) { | ||
st['throws'](function () { | ||
qs.stringify({}, { encoder: 'string' }); | ||
}, new TypeError('Encoder has to be a function.')); | ||
st.end(); | ||
}); | ||
t.test('can use custom encoder for a buffer object', { skip: typeof Buffer === 'undefined' }, function (st) { | ||
st.equal(qs.stringify({ a: SaferBuffer.from([1]) }, { | ||
encoder: function (buffer) { | ||
if (typeof buffer === 'string') { | ||
return buffer; | ||
} | ||
return String.fromCharCode(buffer.readUInt8(0) + 97); | ||
} | ||
}), 'a=b'); | ||
st.end(); | ||
}); | ||
t.test('serializeDate option', function (st) { | ||
var date = new Date(); | ||
st.equal( | ||
qs.stringify({ a: date }), | ||
'a=' + date.toISOString().replace(/:/g, '%3A'), | ||
'default is toISOString' | ||
); | ||
var mutatedDate = new Date(); | ||
mutatedDate.toISOString = function () { | ||
throw new SyntaxError(); | ||
}; | ||
st['throws'](function () { | ||
mutatedDate.toISOString(); | ||
}, SyntaxError); | ||
st.equal( | ||
qs.stringify({ a: mutatedDate }), | ||
'a=' + Date.prototype.toISOString.call(mutatedDate).replace(/:/g, '%3A'), | ||
'toISOString works even when method is not locally present' | ||
); | ||
expect(Qs.stringify({ a: 'c', z: 'y', b : 'f' }, { sort : sort })).to.equal('a=c&b=f&z=y'); | ||
expect(Qs.stringify({ a: 'c', z: { j: 'a', i:'b' }, b : 'f' }, { sort : sort })).to.equal('a=c&b=f&z%5Bi%5D=b&z%5Bj%5D=a'); | ||
done(); | ||
var specificDate = new Date(6); | ||
st.equal( | ||
qs.stringify( | ||
{ a: specificDate }, | ||
{ serializeDate: function (d) { return d.getTime() * 7; } } | ||
), | ||
'a=42', | ||
'custom serializeDate function called' | ||
); | ||
st.end(); | ||
}); | ||
t.test('RFC 1738 spaces serialization', function (st) { | ||
st.equal(qs.stringify({ a: 'b c' }, { format: qs.formats.RFC1738 }), 'a=b+c'); | ||
st.equal(qs.stringify({ 'a b': 'c d' }, { format: qs.formats.RFC1738 }), 'a+b=c+d'); | ||
st.end(); | ||
}); | ||
t.test('RFC 3986 spaces serialization', function (st) { | ||
st.equal(qs.stringify({ a: 'b c' }, { format: qs.formats.RFC3986 }), 'a=b%20c'); | ||
st.equal(qs.stringify({ 'a b': 'c d' }, { format: qs.formats.RFC3986 }), 'a%20b=c%20d'); | ||
st.end(); | ||
}); | ||
t.test('Backward compatibility to RFC 3986', function (st) { | ||
st.equal(qs.stringify({ a: 'b c' }), 'a=b%20c'); | ||
st.end(); | ||
}); | ||
t.test('Edge cases and unknown formats', function (st) { | ||
['UFO1234', false, 1234, null, {}, []].forEach( | ||
function (format) { | ||
st['throws']( | ||
function () { | ||
qs.stringify({ a: 'b c' }, { format: format }); | ||
}, | ||
new TypeError('Unknown format option provided.') | ||
); | ||
} | ||
); | ||
st.end(); | ||
}); | ||
t.test('encodeValuesOnly', function (st) { | ||
st.equal( | ||
qs.stringify( | ||
{ a: 'b', c: ['d', 'e=f'], f: [['g'], ['h']] }, | ||
{ encodeValuesOnly: true } | ||
), | ||
'a=b&c[0]=d&c[1]=e%3Df&f[0][0]=g&f[1][0]=h' | ||
); | ||
st.equal( | ||
qs.stringify( | ||
{ a: 'b', c: ['d', 'e'], f: [['g'], ['h']] } | ||
), | ||
'a=b&c%5B0%5D=d&c%5B1%5D=e&f%5B0%5D%5B0%5D=g&f%5B1%5D%5B0%5D=h' | ||
); | ||
st.end(); | ||
}); | ||
t.test('encodeValuesOnly - strictNullHandling', function (st) { | ||
st.equal( | ||
qs.stringify( | ||
{ a: { b: null } }, | ||
{ encodeValuesOnly: true, strictNullHandling: true } | ||
), | ||
'a[b]' | ||
); | ||
st.end(); | ||
}); | ||
t.test('does not mutate the options argument', function (st) { | ||
var options = {}; | ||
qs.stringify({}, options); | ||
st.deepEqual(options, {}); | ||
st.end(); | ||
}); | ||
t.end(); | ||
}); |
@@ -1,28 +0,34 @@ | ||
// Load modules | ||
'use strict'; | ||
var Code = require('code'); | ||
var Lab = require('lab'); | ||
var Utils = require('../lib/utils'); | ||
var test = require('tape'); | ||
var utils = require('../lib/utils'); | ||
test('merge()', function (t) { | ||
t.deepEqual(utils.merge({ a: 'b' }, { a: 'c' }), { a: ['b', 'c'] }, 'merges two objects with the same key'); | ||
// Declare internals | ||
var oneMerged = utils.merge({ foo: 'bar' }, { foo: { first: '123' } }); | ||
t.deepEqual(oneMerged, { foo: ['bar', { first: '123' }] }, 'merges a standalone and an object into an array'); | ||
var internals = {}; | ||
var twoMerged = utils.merge({ foo: ['bar', { first: '123' }] }, { foo: { second: '456' } }); | ||
t.deepEqual(twoMerged, { foo: { 0: 'bar', 1: { first: '123' }, second: '456' } }, 'merges a standalone and two objects into an array'); | ||
var sandwiched = utils.merge({ foo: ['bar', { first: '123', second: '456' }] }, { foo: 'baz' }); | ||
t.deepEqual(sandwiched, { foo: ['bar', { first: '123', second: '456' }, 'baz'] }, 'merges an object sandwiched by two standalones into an array'); | ||
// Test shortcuts | ||
var nestedArrays = utils.merge({ foo: ['baz'] }, { foo: ['bar', 'xyzzy'] }); | ||
t.deepEqual(nestedArrays, { foo: ['baz', 'bar', 'xyzzy'] }); | ||
var lab = exports.lab = Lab.script(); | ||
var expect = Code.expect; | ||
var describe = lab.experiment; | ||
var it = lab.test; | ||
t.end(); | ||
}); | ||
test('assign()', function (t) { | ||
var target = { a: 1, b: 2 }; | ||
var source = { b: 3, c: 4 }; | ||
var result = utils.assign(target, source); | ||
describe('merge()', function () { | ||
t.equal(result, target, 'returns the target'); | ||
t.deepEqual(target, { a: 1, b: 3, c: 4 }, 'target and source are merged'); | ||
t.deepEqual(source, { b: 3, c: 4 }, 'source is untouched'); | ||
it('can merge two objects with the same key', function (done) { | ||
expect(Utils.merge({ a: 'b' }, { a: 'c' })).to.deep.equal({ a: ['b', 'c'] }); | ||
done(); | ||
}); | ||
t.end(); | ||
}); |
var qs = require('qs'); | ||
function combineLoaders(loaders) { | ||
return loaders.map(function(loaderEntry) { | ||
var query = qs.stringify(loaderEntry.query, { arrayFormat: 'brackets', encode: false }); | ||
if (query) { | ||
query = '?' + query; | ||
} | ||
return loaderEntry.loader + query; | ||
}).join('!'); | ||
return loaders | ||
.map(function(loaderEntry) { | ||
if (typeof loaderEntry === 'string') { | ||
return loaderEntry; | ||
} | ||
var query = qs.stringify(loaderEntry.options || loaderEntry.query, { | ||
arrayFormat: 'brackets', | ||
encode: false, | ||
}); | ||
if (query) { | ||
query = '?' + query; | ||
} | ||
return loaderEntry.loader + query; | ||
}) | ||
.join('!'); | ||
} | ||
module.exports = combineLoaders; |
@@ -5,8 +5,8 @@ { | ||
{ | ||
"raw": "webpack-combine-loaders@2.0.0", | ||
"raw": "webpack-combine-loaders@2.0.4", | ||
"scope": null, | ||
"escapedName": "webpack-combine-loaders", | ||
"name": "webpack-combine-loaders", | ||
"rawSpec": "2.0.0", | ||
"spec": "2.0.0", | ||
"rawSpec": "2.0.4", | ||
"spec": "2.0.4", | ||
"type": "version" | ||
@@ -17,24 +17,24 @@ }, | ||
], | ||
"_from": "webpack-combine-loaders@2.0.0", | ||
"_id": "webpack-combine-loaders@2.0.0", | ||
"_from": "webpack-combine-loaders@2.0.4", | ||
"_id": "webpack-combine-loaders@2.0.4", | ||
"_inCache": true, | ||
"_location": "/webpack-combine-loaders", | ||
"_nodeVersion": "4.1.1", | ||
"_nodeVersion": "8.11.3", | ||
"_npmOperationalInternal": { | ||
"host": "packages-13-west.internal.npmjs.com", | ||
"tmp": "tmp/webpack-combine-loaders-2.0.0.tgz_1458470740708_0.4458477725274861" | ||
"host": "s3://npm-registry-packages", | ||
"tmp": "tmp/webpack-combine-loaders_2.0.4_1535285038454_0.36518350045843273" | ||
}, | ||
"_npmUser": { | ||
"name": "jsdf", | ||
"email": "james@jsdf.co" | ||
"name": "vernondegoede", | ||
"email": "info@vernondegoede.com" | ||
}, | ||
"_npmVersion": "2.14.4", | ||
"_npmVersion": "6.4.0", | ||
"_phantomChildren": {}, | ||
"_requested": { | ||
"raw": "webpack-combine-loaders@2.0.0", | ||
"raw": "webpack-combine-loaders@2.0.4", | ||
"scope": null, | ||
"escapedName": "webpack-combine-loaders", | ||
"name": "webpack-combine-loaders", | ||
"rawSpec": "2.0.0", | ||
"spec": "2.0.0", | ||
"rawSpec": "2.0.4", | ||
"spec": "2.0.4", | ||
"type": "version" | ||
@@ -45,19 +45,26 @@ }, | ||
], | ||
"_resolved": "http://registry.npmjs.org/webpack-combine-loaders/-/webpack-combine-loaders-2.0.0.tgz", | ||
"_shasum": "c8cd033ec797748b569c7823f54d4481670f3e05", | ||
"_resolved": "http://registry.npmjs.org/webpack-combine-loaders/-/webpack-combine-loaders-2.0.4.tgz", | ||
"_shasum": "27814d52b8329ed6565be39009aac76361e7e22c", | ||
"_shrinkwrap": null, | ||
"_spec": "webpack-combine-loaders@2.0.0", | ||
"_spec": "webpack-combine-loaders@2.0.4", | ||
"_where": "/home/travis/build/kentcdodds/webpack-config-utils", | ||
"author": "", | ||
"dependencies": { | ||
"qs": "^5.2.0" | ||
"qs": "^6.5.2" | ||
}, | ||
"description": "Converts an array of loaders defined using the `{loader, query}` object syntax into a single loader string. Useful for dealing with plugins which only understand the loader string syntax.", | ||
"devDependencies": {}, | ||
"devDependencies": { | ||
"eslint": "^3.9.1", | ||
"tap": "^8.0.0" | ||
}, | ||
"directories": {}, | ||
"dist": { | ||
"shasum": "c8cd033ec797748b569c7823f54d4481670f3e05", | ||
"tarball": "https://registry.npmjs.org/webpack-combine-loaders/-/webpack-combine-loaders-2.0.0.tgz" | ||
"integrity": "sha512-5O5PYVE5tZ3I3uUm3QB7niLEJzLketl8hvAcJwa4YmwNWS/vixfVsqhtUaBciP8J4u/GwIHV52d7kkgZJFvDnw==", | ||
"shasum": "27814d52b8329ed6565be39009aac76361e7e22c", | ||
"tarball": "https://registry.npmjs.org/webpack-combine-loaders/-/webpack-combine-loaders-2.0.4.tgz", | ||
"fileCount": 8, | ||
"unpackedSize": 76487, | ||
"npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbgpcuCRA9TVsSAnZWagAACu0P+gPUF4yzmcBSif53fEV8\n/eT++LhC1/5CEv3wqhX1iTXqZ1sLnPvyqtV1h64Ke1KzNLO4b5Q9mVeHD38t\nE29VhFcQ1PijdxF7ibFZontS3YYKACECO+qnlhslaMvptB5ucZbTAhy2EYeZ\nPRehilVEwLFQMPny1afnaxi+j+HS67JJMIHpZ/CmC/LOqJ2+tKZD/v4QKtDU\npjYLcEYKKzKqo0h9m5vahhhQDJiNqMOGFx3uukRRep84NrFERlta52ohIdiD\nNlW9ZM75ztVqfkvy4IArxs7QIGRwt4sn1ad7twSvHzPTJfEuNz7gQI6w32kQ\nKttNI/DpPrJGbUIgVR1nMrTcea6R/f6rrWZ/UQELNbTPbnuCGG/OrZxzlBSb\nlJGHvfQGU4YSPg96HrzqEwYQb25H6WIkX7GN4nDFkldGTxzIYysXSzLZi8so\n2t3L6OQ2tSrQrvnASGaxEC/KxUirMoGHGQ6D1l9uBPM6Hmjgqo2niLfL+XhI\nDFYso7/eKMqYT8bDCL6xGiqjWUB0VBgoYGa0zLqKH/Fvw5TDDNFhIeNzj/57\ntyCrxAmG7KRL0JDTvTvSwcHbIe6/r12wc/Ac3iNd8EZRYoVqLzpf8CQLuYtU\ncM3loVUUBBO8CveDDQXPnI8ih28QpVV6zAnEIRwaqkfDuiHPUDlp+pio07kQ\nXv41\r\n=wn36\r\n-----END PGP SIGNATURE-----\r\n" | ||
}, | ||
"gitHead": "adca9e40aaebccccb40a7a0e0ea9e798c215763a", | ||
"gitHead": "427f6b7722e464a75ab1d67659c3bdd159904143", | ||
"license": "ISC", | ||
@@ -75,5 +82,8 @@ "main": "combineLoaders.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
"lint": "eslint .", | ||
"lint-fix": "eslint . --fix", | ||
"tap": "tap test", | ||
"test": "eslint . && tap test" | ||
}, | ||
"version": "2.0.0" | ||
"version": "2.0.4" | ||
} |
@@ -26,3 +26,3 @@ # webpack-combine-loaders | ||
]); | ||
// => 'css-loader?modules=true&sourceMap=true&localIdentName=%5Bname%5D__%5Blocal%5D--%5Bhash%3Abase64%3A5%5D!sass-loader?sourceMap=true&includePaths%5B%5D=app%2Fassets%2Fstylesheets&includePaths%5B%5D=app%2Fassets%2Fstylesheets%2Flegacy' | ||
// => 'css-loader?modules=true&sourceMap=true&localIdentName=[name]__[local]--[hash:base64:5]!sass-loader?sourceMap=true&includePaths[]=app/assets/stylesheets&includePaths[]=app/assets/stylesheets/legacy' | ||
``` | ||
@@ -96,3 +96,5 @@ ## why? | ||
- 2.0.4 - Upgrade `qs` dependency ([#10](https://github.com/jsdf/webpack-combine-loaders/pull/10)). Drops support for Node < 4. | ||
- 2.0.3 - add support for webpack2 loader "options" (with fallback to "query") | ||
- 2.0.0 - no longer uri-encodes loader params | ||
- 1.0.0 - initial release |
{ | ||
"name": "webpack-config-utils", | ||
"version": "2.3.0", | ||
"version": "2.3.1", | ||
"description": "Utilities to help your webpack config be easier to read", | ||
"main": "dist/index.js", | ||
"scripts": { | ||
"start": "package-scripts", | ||
"test": "package-scripts test" | ||
"start": "nps", | ||
"test": "nps test" | ||
}, | ||
@@ -17,3 +17,3 @@ "files": [ | ||
"dependencies": { | ||
"webpack-combine-loaders": "2.0.0" | ||
"webpack-combine-loaders": "2.0.4" | ||
}, | ||
@@ -40,6 +40,6 @@ "bundledDependencies": [ | ||
"lodash": "4.13.1", | ||
"nps": "^5.3.1", | ||
"nps-utils": "^1.2.0", | ||
"nyc": "^7.0.0", | ||
"opt-cli": "^1.4.2", | ||
"p-s": "^1.0.4", | ||
"rimraf": "^2.5.2", | ||
"semantic-release": "^6.2.1", | ||
@@ -46,0 +46,0 @@ "validate-commit-msg": "^2.6.1" |
@@ -12,3 +12,3 @@ # webpack-config-utils | ||
[![All Contributors](https://img.shields.io/badge/all_contributors-5-orange.svg?style=flat-square)](#contributors) | ||
[![All Contributors](https://img.shields.io/badge/all_contributors-8-orange.svg?style=flat-square)](#contributors) | ||
[![PRs Welcome][prs-badge]][prs] | ||
@@ -54,27 +54,35 @@ [![Donate][donate-badge]][donate] | ||
const {ifProduction} = getIfUtils(process.env.NODE_ENV) | ||
const {ifProduction, ifNotProduction} = getIfUtils(process.env.NODE_ENV) | ||
module.exports = { | ||
// ... your config | ||
mode: ifProduction('production', 'development'), | ||
entry: removeEmpty({ | ||
app: ifProd('./indexWithoutCSS', './indexWithCSS'), | ||
css: ifProd('./style.scss') | ||
app: ifProduction('./indexWithoutCSS', './indexWithCSS'), | ||
css: ifProduction('./style.scss') | ||
}), | ||
output: { | ||
chunkFilename: ifProduction('js/[id].[contenthash].js', 'js/[name].js'), | ||
filename: ifProduction('js/[id].[contenthash].js', 'js/[name].js'), | ||
}, | ||
module: { | ||
loaders: [ | ||
removeEmpty({ | ||
test: /\.scss$/, | ||
loader: ifProd(ExtractTextPlugin.extract({ | ||
loader: ['css-loader', 'sass-loader'] | ||
})), | ||
loaders: ifNotProd(['style-loader', 'css-loader', 'sass-loader']) | ||
}) | ||
] | ||
rules: [ | ||
{ | ||
test: /\.(sc|c)ss$/, | ||
exclude: /node_modules/, | ||
use: removeEmpty([ | ||
ifProduction(MiniCssExtractPlugin.loader), | ||
ifNotProduction({loader: 'style-loader', options: {sourceMap: true}}), | ||
{loader: 'css-loader', options: {sourceMap: true}}, | ||
{loader: 'postcss-loader', options: {sourceMap: true}}, | ||
{loader: 'sass-loader', options: {sourceMap: true}}, | ||
]), | ||
}, | ||
}, | ||
plugins: removeEmpty([ | ||
ifProduction(new webpack.optimize.DedupePlugin()), | ||
ifProduction(new webpack.LoaderOptionsPlugin({ | ||
minimize: true, | ||
quiet: true, | ||
})), | ||
ifProduction( | ||
new MiniCssExtractPlugin({ | ||
filename: 'css/[id].[contenthash].css', | ||
}) | ||
), | ||
ifProduction(new webpack.DefinePlugin({ | ||
@@ -85,8 +93,2 @@ 'process.env': { | ||
})), | ||
ifProduction(new webpack.optimize.UglifyJsPlugin({ | ||
compress: { | ||
screw_ie8: true, | ||
warnings: false, | ||
}, | ||
})), | ||
new HtmlWebpackPlugin({ | ||
@@ -102,3 +104,3 @@ template: './index.html', | ||
Then you'd invoke the webpack config with [`cross-env`][cross-env] in your `package.json` scripts (or with | ||
[`p-s`][p-s]): | ||
[`nps`][nps]): | ||
@@ -117,3 +119,3 @@ ```js | ||
Things get even better with webpack 2 because you can write your webpack config as a function that accepts an `env` | ||
Things get even better with webpack 2+ because you can write your webpack config as a function that accepts an `env` | ||
argument which you can set on the command line (which means you don't need `cross-env` π). | ||
@@ -144,2 +146,6 @@ | ||
## Articles | ||
* [One webpack config to rule them allβββenvironments that is](https://medium.com/@ryandrewjohnson/one-webpack-config-to-rule-them-all-environments-that-is-277457769779#.34laieb5i) | ||
## Contributors | ||
@@ -150,4 +156,5 @@ | ||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --> | ||
| [<img src="https://avatars.githubusercontent.com/u/1500684?v=3" width="100px;"/><br /><sub>Kent C. Dodds</sub>](https://kentcdodds.com)<br />[π»](https://github.com/kentcdodds/webpack-config-utils/commits?author=kentcdodds) [π](https://github.com/kentcdodds/webpack-config-utils/commits?author=kentcdodds) π‘ π [β οΈ](https://github.com/kentcdodds/webpack-config-utils/commits?author=kentcdodds) | [<img src="https://avatars.githubusercontent.com/u/284515?v=3" width="100px;"/><br /><sub>Breno Calazans</sub>](https://twitter.com/breno_calazans)<br />π‘ | [<img src="https://avatars.githubusercontent.com/u/363583?v=3" width="100px;"/><br /><sub>Tamara Temple</sub>](http://tamouse.org)<br />[π](https://github.com/kentcdodds/webpack-config-utils/commits?author=tamouse) | [<img src="https://avatars.githubusercontent.com/u/7907232?v=3" width="100px;"/><br /><sub>Ben Halverson</sub>](benhalverson.me)<br />[π](https://github.com/kentcdodds/webpack-config-utils/commits?author=benhalverson) | [<img src="https://avatars.githubusercontent.com/u/7352279?v=3" width="100px;"/><br /><sub>Huy Nguyen</sub>](http://www.huy-nguyen.com/)<br />[π»](https://github.com/kentcdodds/webpack-config-utils/commits?author=huy-nguyen) [π](https://github.com/kentcdodds/webpack-config-utils/commits?author=huy-nguyen) π‘ [β οΈ](https://github.com/kentcdodds/webpack-config-utils/commits?author=huy-nguyen) | | ||
| :---: | :---: | :---: | :---: | :---: | | ||
| [<img src="https://avatars.githubusercontent.com/u/1500684?v=3" width="100px;"/><br /><sub>Kent C. Dodds</sub>](https://kentcdodds.com)<br />[π»](https://github.com/kentcdodds/webpack-config-utils/commits?author=kentcdodds) [π](https://github.com/kentcdodds/webpack-config-utils/commits?author=kentcdodds) π‘ π [β οΈ](https://github.com/kentcdodds/webpack-config-utils/commits?author=kentcdodds) | [<img src="https://avatars.githubusercontent.com/u/284515?v=3" width="100px;"/><br /><sub>Breno Calazans</sub>](https://twitter.com/breno_calazans)<br />π‘ | [<img src="https://avatars.githubusercontent.com/u/363583?v=3" width="100px;"/><br /><sub>Tamara Temple</sub>](http://tamouse.org)<br />[π](https://github.com/kentcdodds/webpack-config-utils/commits?author=tamouse) | [<img src="https://avatars.githubusercontent.com/u/7907232?v=3" width="100px;"/><br /><sub>Ben Halverson</sub>](benhalverson.me)<br />[π](https://github.com/kentcdodds/webpack-config-utils/commits?author=benhalverson) | [<img src="https://avatars.githubusercontent.com/u/7352279?v=3" width="100px;"/><br /><sub>Huy Nguyen</sub>](http://www.huy-nguyen.com/)<br />[π»](https://github.com/kentcdodds/webpack-config-utils/commits?author=huy-nguyen) [π](https://github.com/kentcdodds/webpack-config-utils/commits?author=huy-nguyen) π‘ [β οΈ](https://github.com/kentcdodds/webpack-config-utils/commits?author=huy-nguyen) | [<img src="https://avatars.githubusercontent.com/u/3419547?v=3" width="100px;"/><br /><sub>Ryan Johnson</sub>](https://github.com/ryandrewjohnson)<br />π [π](https://github.com/kentcdodds/webpack-config-utils/commits?author=ryandrewjohnson) | [<img src="https://avatars1.githubusercontent.com/u/97462?v=3" width="100px;"/><br /><sub>Adam DiCarlo</sub>](http://adamdicarlo.com)<br />[π](https://github.com/kentcdodds/webpack-config-utils/commits?author=adamdicarlo) π§ | | ||
| :---: | :---: | :---: | :---: | :---: | :---: | :---: | | ||
| [<img src="https://avatars2.githubusercontent.com/u/5779101?v=4" width="100px;"/><br /><sub>Jeremy Y</sub>](https://github.com/jezzay)<br />[π](https://github.com/kentcdodds/webpack-config-utils/commits?author=jezzay) | | ||
<!-- ALL-CONTRIBUTORS-LIST:END --> | ||
@@ -188,3 +195,3 @@ | ||
[cross-env]: https://www.npmjs.com/package/cross-env | ||
[p-s]: https://www.npmjs.com/package/p-s | ||
[nps]: https://www.npmjs.com/package/nps | ||
[API Docs]: https://doclets.io/kentcdodds/webpack-config-utils/master |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
217877
34
2364
190
1