Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
zepod committed Mar 4, 2019
2 parents 5ba6d27 + c138b11 commit d893366
Show file tree
Hide file tree
Showing 4 changed files with 224 additions and 5 deletions.
135 changes: 135 additions & 0 deletions index.d.ts
@@ -0,0 +1,135 @@
export interface ParseOptions {
/**
* Decode the keys and values. URI components are decoded with [`decode-uri-component`](https://github.com/SamVerschueren/decode-uri-component).
*
* @default true
*/
readonly decode?: boolean;

/**
* Supports both `index` for an indexed array representation or `bracket` for a *bracketed* array representation.
*
* @default 'none'
*
* - `bracket`: stands for parsing correctly arrays with bracket representation on the query string, such as:
*
*
* queryString.parse('foo[]=1&foo[]=2&foo[]=3', {arrayFormat: 'bracket'});
* //=> foo: [1,2,3]
*
* - `index`: stands for parsing taking the index into account, such as:
*
*
* queryString.parse('foo[0]=1&foo[1]=2&foo[3]=3', {arrayFormat: 'index'});
* //=> foo: [1,2,3]
*
* - `none`: is the **default** option and removes any bracket representation, such as:
*
*
* queryString.parse('foo=1&foo=2&foo=3');
* //=> foo: [1,2,3]
*/
readonly arrayFormat?: 'bracket' | 'index' | 'none';
}

export interface ParsedQuery {
readonly [key: string]: string | string[] | undefined;
}

/**
* Parse a query string into an object. Leading `?` or `#` are ignored, so you can pass `location.search` or `location.hash` directly.
*
* The returned object is created with [`Object.create(null)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create) and thus does not have a `prototype`.
*
* @param query - The query string to parse.
*/
export function parse(query: string, options?: ParseOptions): ParsedQuery;

export interface ParsedUrl {
readonly url: string;
readonly query: ParsedQuery;
}

/**
* Extract the URL and the query string as an object.
*
* @param url - The URL to parse.
*
* @example
*
* queryString.parseUrl('https://foo.bar?foo=bar');
* //=> {url: 'https://foo.bar', query: {foo: 'bar'}}
*/
export function parseUrl(url: string, options?: ParseOptions): ParsedUrl;

export interface StringifyOptions {
/**
* Strictly encode URI components with [`strict-uri-encode`](https://github.com/kevva/strict-uri-encode). It uses [`encodeURIComponent`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent) if set to `false`. You probably [don't care](https://github.com/sindresorhus/query-string/issues/42) about this option.
*
* @default true
*/
readonly strict?: boolean;

/**
* [URL encode](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent) the keys and values.
*
* @default true
*/
readonly encode?: boolean;

/**
* Supports both `index` for an indexed array representation or `bracket` for a *bracketed* array representation.
*
* @default 'none'
*
* - `bracket`: stands for parsing correctly arrays with bracket representation on the query string, such as:
*
*
* queryString.stringify({foo: [1,2,3]}, {arrayFormat: 'bracket'});
* // => foo[]=1&foo[]=2&foo[]=3
*
* - `index`: stands for parsing taking the index into account, such as:
*
*
* queryString.stringify({foo: [1,2,3]}, {arrayFormat: 'index'});
* // => foo[0]=1&foo[1]=2&foo[3]=3
*
* - `none`: is the **default** option and removes any bracket representation, such as:
*
*
* queryString.stringify({foo: [1,2,3]});
* // => foo=1&foo=2&foo=3
*/
readonly arrayFormat?: 'bracket' | 'index' | 'none';

/**
* Supports both `Function` as a custom sorting function or `false` to disable sorting.
*
* If omitted, keys are sorted using `Array#sort`, which means, converting them to strings and comparing strings in Unicode code point order.
*
* @example
*
* const order = ['c', 'a', 'b'];
* queryString.stringify({ a: 1, b: 2, c: 3}, {
* sort: (itemLeft, itemRight) => order.indexOf(itemLeft) - order.indexOf(itemRight)
* });
* // => 'c=3&a=1&b=2'
*
* queryString.stringify({ b: 1, c: 2, a: 3}, {sort: false});
* // => 'b=1&c=2&a=3'
*/
readonly sort?: ((itemLeft: string, itemRight: string) => number) | false;
}

/**
* Stringify an object into a query string, sorting the keys.
*/
export function stringify(
object: {[key: string]: unknown},
options?: StringifyOptions
): string;

/**
* Extract a query string from a URL that can be passed into `.parse()`.
*/
export function extract(url: string): string;
14 changes: 14 additions & 0 deletions index.js
Expand Up @@ -10,42 +10,53 @@ function encoderForArrayFormat(options) {
if (value === undefined) {
return result;
}

if (value === null) {
return [...result, [encode(key, options), '[', index, ']'].join('')];
}

return [
...result,
[encode(key, options), '[', encode(index, options), ']=', encode(value, options)].join('')
];
};

case 'bracket':
return key => (result, value) => {
if (value === undefined) {
return result;
}

if (value === null) {
return [...result, [encode(key, options), '[]'].join('')];
}

return [...result, [encode(key, options), '[]=', encode(value, options)].join('')];
};

case 'comma':
return key => (result, value, index) => {
if (!value) {
return result;
}

if (index === 0) {
return [[encode(key, options), '=', encode(value, options)].join('')];
}

return [[result, encode(value, options)].join(',')];
};

default:
return key => (result, value) => {
if (value === undefined) {
return result;
}

if (value === null) {
return [...result, encode(key, options)];
}

return [...result, [encode(key, options), '=', encode(value, options)].join('')];
};
}
Expand All @@ -72,6 +83,7 @@ function parserForArrayFormat(options) {

accumulator[key][result[1]] = value;
};

case 'bracket':
return (key, value, accumulator) => {
result = /(\[\])$/.exec(key);
Expand All @@ -89,12 +101,14 @@ function parserForArrayFormat(options) {

accumulator[key] = [].concat(accumulator[key], value);
};

case 'comma':
return (key, value, accumulator) => {
const isArray = typeof value === 'string' && value.split('').indexOf(',') > -1;
const newValue = isArray ? value.split(',') : value;
accumulator[key] = newValue;
};

default:
return (key, value, accumulator) => {
if (accumulator[key] === undefined) {
Expand Down
68 changes: 68 additions & 0 deletions index.test-d.ts
@@ -0,0 +1,68 @@
import {expectType} from 'tsd-check';
import * as queryString from '.';

// Stringify
expectType<string>(
queryString.stringify({
str: 'bar',
strArray: ['baz'],
num: 123,
numArray: [456],
bool: true,
boolArray: [false]
})
);

expectType<string>(queryString.stringify({foo: 'bar'}, {strict: false}));
expectType<string>(queryString.stringify({foo: 'bar'}, {encode: false}));
expectType<string>(
queryString.stringify({foo: 'bar'}, {arrayFormat: 'bracket'})
);
expectType<string>(queryString.stringify({foo: 'bar'}, {arrayFormat: 'index'}));
expectType<string>(queryString.stringify({foo: 'bar'}, {arrayFormat: 'none'}));
expectType<string>(queryString.stringify({foo: 'bar'}, {sort: false}));
const order = ['c', 'a', 'b'];
expectType<string>(
queryString.stringify(
{foo: 'bar'},
{
sort: (itemLeft, itemRight) =>
order.indexOf(itemLeft) - order.indexOf(itemRight)
}
)
);

// Parse
expectType<queryString.ParsedQuery>(queryString.parse('?foo=bar'));

expectType<queryString.ParsedQuery>(
queryString.parse('?foo=bar', {decode: false})
);
expectType<queryString.ParsedQuery>(
queryString.parse('?foo=bar', {arrayFormat: 'bracket'})
);
expectType<queryString.ParsedQuery>(
queryString.parse('?foo=bar', {arrayFormat: 'index'})
);
expectType<queryString.ParsedQuery>(
queryString.parse('?foo=bar', {arrayFormat: 'none'})
);

// Parse URL
expectType<queryString.ParsedUrl>(queryString.parseUrl('?foo=bar'));

expectType<queryString.ParsedUrl>(
queryString.parseUrl('?foo=bar', {decode: false})
);
expectType<queryString.ParsedUrl>(
queryString.parseUrl('?foo=bar', {arrayFormat: 'bracket'})
);
expectType<queryString.ParsedUrl>(
queryString.parseUrl('?foo=bar', {arrayFormat: 'index'})
);
expectType<queryString.ParsedUrl>(
queryString.parseUrl('?foo=bar', {arrayFormat: 'none'})
);

// Extract
expectType<string>(queryString.extract('http://foo.bar/?abc=def&hij=klm'));
12 changes: 7 additions & 5 deletions package.json
@@ -1,6 +1,6 @@
{
"name": "query-string",
"version": "6.2.0",
"version": "6.3.0",
"description": "Parse and stringify URL query strings",
"license": "MIT",
"repository": "sindresorhus/query-string",
Expand All @@ -13,10 +13,11 @@
"node": ">=6"
},
"scripts": {
"test": "xo && ava"
"test": "xo && ava && tsd-check"
},
"files": [
"index.js"
"index.js",
"index.d.ts"
],
"keywords": [
"browser",
Expand All @@ -38,9 +39,10 @@
"strict-uri-encode": "^2.0.0"
},
"devDependencies": {
"ava": "^0.25.0",
"ava": "^1.2.1",
"deep-equal": "^1.0.1",
"fast-check": "^1.5.0",
"xo": "^0.23.0"
"tsd-check": "^0.3.0",
"xo": "^0.24.0"
}
}

0 comments on commit d893366

Please sign in to comment.