Skip to content

Commit

Permalink
Merge pull request #14 from cdeutsch/sync-query-string-v6.13.0
Browse files Browse the repository at this point in the history
Sync query string v6.13.0
  • Loading branch information
cdeutsch authored Jun 9, 2020
2 parents da83806 + 387607a commit 57f4744
Show file tree
Hide file tree
Showing 11 changed files with 222 additions and 15 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
language: node_js
node_js:
- '14'
- '12'
- '10'
- '8'
Expand Down
72 changes: 72 additions & 0 deletions benchmark.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
'use strict';
const Benchmark = require('benchmark');
const queryString = require('.');

const {stringify, stringifyUrl} = queryString;
const suite = new Benchmark.Suite();

// Fixtures
const TEST_OBJECT = {
genre: 'Epic fantasy',
author: '',
page: 2,
published: true,
symbols: 'πµ',
chapters: [1, 2, 3],
none: null
};
const TEST_HOST = 'https://foo.bar/';
const TEST_STRING = stringify(TEST_OBJECT);
const TEST_BRACKETS_STRING = stringify(TEST_OBJECT, {arrayFormat: 'bracket'});
const TEST_INDEX_STRING = stringify(TEST_OBJECT, {arrayFormat: 'index'});
const TEST_COMMA_STRING = stringify(TEST_OBJECT, {arrayFormat: 'comma'});
const TEST_URL = stringifyUrl({url: TEST_HOST, query: TEST_OBJECT});

// Creates a test case and adds it to the suite
const defineTestCase = (methodName, input, options) => {
const fn = queryString[methodName];
const label = options ? ` (${stringify(options)})` : '';

suite.add(methodName + label, () => fn(input, options || {}));
};

// Define all test cases

// Parse
defineTestCase('parse', TEST_STRING);
defineTestCase('parse', TEST_STRING, {parseNumbers: true});
defineTestCase('parse', TEST_STRING, {parseBooleans: true});
defineTestCase('parse', TEST_STRING, {sort: false});
defineTestCase('parse', TEST_STRING, {decode: false});
defineTestCase('parse', TEST_BRACKETS_STRING, {arrayFormat: 'bracket'});
defineTestCase('parse', TEST_INDEX_STRING, {arrayFormat: 'index'});
defineTestCase('parse', TEST_COMMA_STRING, {arrayFormat: 'comma'});

// Stringify
defineTestCase('stringify', TEST_OBJECT);
defineTestCase('stringify', TEST_OBJECT, {strict: false});
defineTestCase('stringify', TEST_OBJECT, {encode: false});
defineTestCase('stringify', TEST_OBJECT, {skipNull: true});
defineTestCase('stringify', TEST_OBJECT, {skipEmptyString: true});
defineTestCase('stringify', TEST_OBJECT, {arrayFormat: 'bracket'});
defineTestCase('stringify', TEST_OBJECT, {arrayFormat: 'index'});
defineTestCase('stringify', TEST_OBJECT, {arrayFormat: 'comma'});

// Extract
defineTestCase('extract', TEST_URL);

// ParseUrl
defineTestCase('parseUrl', TEST_URL);

// StringifyUrl
defineTestCase('stringifyUrl', {url: TEST_HOST, query: TEST_OBJECT});

// Log/display the results
suite.on('cycle', event => {
const {name, hz} = event.target;
const opsPerSec = Math.round(hz).toLocaleString();

console.log(name.padEnd(36, '_') + opsPerSec.padStart(12, '_') + ' ops/s');
});

suite.run();
28 changes: 24 additions & 4 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -476,24 +476,44 @@ exports.stringify = function (object, options) {
};

exports.parseUrl = function (input, options) {
return {
url: removeHash(input).split('?')[0] || '',
options = Object.assign({
decode: true
}, options);

var _splitOnFirst3 = splitOnFirst(input, '#'),
_splitOnFirst4 = _slicedToArray(_splitOnFirst3, 2),
url = _splitOnFirst4[0],
hash = _splitOnFirst4[1];

return Object.assign({
url: url.split('?')[0] || '',
query: parse(extract(input), options)
};
}, options && options.parseFragmentIdentifier && hash ? {
fragmentIdentifier: decode(hash, options)
} : {});
};

exports.stringifyUrl = function (input, options) {
options = Object.assign({
encode: true,
strict: true
}, options);
var url = removeHash(input.url).split('?')[0] || '';
var queryFromUrl = exports.extract(input.url);
var parsedQueryFromUrl = exports.parse(queryFromUrl);
var hash = getHash(input.url);
var query = Object.assign(parsedQueryFromUrl, input.query);
var queryString = exports.stringify(query, options);

if (queryString) {
queryString = "?".concat(queryString);
}

var hash = getHash(input.url);

if (input.fragmentIdentifier) {
hash = "#".concat(encode(input.fragmentIdentifier, options));
}

return "".concat(url).concat(queryString).concat(hash);
};

Expand Down
40 changes: 39 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,21 @@ export interface ParseOptions {
```
*/
readonly parseBooleans?: boolean;

/**
Parse the fragment identifier from the URL and add it to result object.
@default false
@example
```
import queryString = require('query-string');
queryString.parseUrl('https://foo.bar?foo=bar#xyz', {parseFragmentIdentifier: true});
//=> {url: 'https://foo.bar', query: {foo: 'bar'}, fragmentIdentifier: 'xyz'}
```
*/
readonly parseFragmentIdentifier?: boolean;
}

export interface ParsedQuery<T = string> {
Expand All @@ -142,11 +157,20 @@ export function parse(query: string, options?: ParseOptions): ParsedQuery;
export interface ParsedUrl {
readonly url: string;
readonly query: ParsedQuery;

/**
The fragment identifier of the URL.
Present when the `parseFragmentIdentifier` option is `true`.
*/
readonly fragmentIdentifier?: string;
}

/**
Extract the URL and the query string as an object.
If the `parseFragmentIdentifier` option is `true`, the object will also contain a `fragmentIdentifier` property.
@param url - The URL to parse.
@example
Expand All @@ -155,6 +179,9 @@ import queryString = require('query-string');
queryString.parseUrl('https://foo.bar?foo=bar');
//=> {url: 'https://foo.bar', query: {foo: 'bar'}}
queryString.parseUrl('https://foo.bar?foo=bar#xyz', {parseFragmentIdentifier: true});
//=> {url: 'https://foo.bar', query: {foo: 'bar'}, fragmentIdentifier: 'xyz'}
```
*/
export function parseUrl(url: string, options?: ParseOptions): ParsedUrl;
Expand Down Expand Up @@ -298,7 +325,7 @@ export interface StringifyOptions {
});
//=> 'a=1&d=4'
```
@example
```
import queryString = require('query-string');
Expand Down Expand Up @@ -332,13 +359,24 @@ Stringify an object into a URL with a query string and sorting the keys. The inv
Query items in the `query` property overrides queries in the `url` property.
The `fragmentIdentifier` property overrides the fragment identifier in the `url` property.
@example
```
queryString.stringifyUrl({url: 'https://foo.bar', query: {foo: 'bar'}});
//=> 'https://foo.bar?foo=bar'
queryString.stringifyUrl({url: 'https://foo.bar?foo=baz', query: {foo: 'bar'}});
//=> 'https://foo.bar?foo=bar'
queryString.stringifyUrl({
url: 'https://foo.bar',
query: {
top: 'foo'
},
fragmentIdentifier: 'bar'
});
//=> 'https://foo.bar?top=foo#bar'
```
*/
export function stringifyUrl(
Expand Down
29 changes: 24 additions & 5 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -338,22 +338,41 @@ exports.stringify = (object, options) => {
};

exports.parseUrl = (input, options) => {
return {
url: removeHash(input).split('?')[0] || '',
query: parse(extract(input), options)
};
options = Object.assign({
decode: true
}, options);

const [url, hash] = splitOnFirst(input, '#');

return Object.assign(
{
url: url.split('?')[0] || '',
query: parse(extract(input), options)
},
options && options.parseFragmentIdentifier && hash ? {fragmentIdentifier: decode(hash, options)} : {}
);
};

exports.stringifyUrl = (input, options) => {
options = Object.assign({
encode: true,
strict: true
}, options);

const url = removeHash(input.url).split('?')[0] || '';
const queryFromUrl = exports.extract(input.url);
const parsedQueryFromUrl = exports.parse(queryFromUrl);
const hash = getHash(input.url);

const query = Object.assign(parsedQueryFromUrl, input.query);
let queryString = exports.stringify(query, options);
if (queryString) {
queryString = `?${queryString}`;
}

let hash = getHash(input.url);
if (input.fragmentIdentifier) {
hash = `#${encode(input.fragmentIdentifier, options)}`;
}

return `${url}${queryString}${hash}`;
};
3 changes: 3 additions & 0 deletions index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ expectType<queryString.ParsedUrl>(
expectType<queryString.ParsedUrl>(
queryString.parseUrl('?foo=true', {parseBooleans: true})
);
expectType<queryString.ParsedUrl>(
queryString.parseUrl('?foo=true#bar', {parseFragmentIdentifier: true})
);

// Extract
expectType<string>(queryString.extract('http://foo.bar/?abc=def&hij=klm'));
2 changes: 1 addition & 1 deletion license
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) Sindre Sorhus <[email protected]> (sindresorhus.com)
Copyright (c) Sindre Sorhus <[email protected]> (http://sindresorhus.com)

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
{
"name": "query-string-for-all",
"version": "6.12.1",
"version": "6.13.0",
"description": "Parse and stringify URL query strings",
"license": "MIT",
"repository": "cdeutsch/query-string-for-all",
"author": {
"name": "Sindre Sorhus",
"email": "[email protected]",
"url": "sindresorhus.com"
"url": "https://sindresorhus.com"
},
"engines": {
"node": ">=6"
},
"main": "dist/index.js",
"scripts": {
"benchmark": "node benchmark.js",
"build": "webpack --mode production",
"test": "npm run build && xo && ava && tsd"
},
Expand Down Expand Up @@ -45,6 +46,7 @@
"ava": "^1.4.1",
"babel-loader": "^8.0.6",
"decode-uri-component": "^0.2.0",
"benchmark": "^2.1.4",
"deep-equal": "^1.0.1",
"fast-check": "^1.5.0",
"split-on-first": "^1.0.0",
Expand Down
40 changes: 38 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -310,15 +310,40 @@ Note: This behaviour can be changed with the `skipNull` option.

Extract the URL and the query string as an object.

The `options` are the same as for `.parse()`.

Returns an object with a `url` and `query` property.

If the `parseFragmentIdentifier` option is `true`, the object will also contain a `fragmentIdentifier` property.

```js
const queryString = require('query-string');

queryString.parseUrl('https://foo.bar?foo=bar');
//=> {url: 'https://foo.bar', query: {foo: 'bar'}}

queryString.parseUrl('https://foo.bar?foo=bar#xyz', {parseFragmentIdentifier: true});
//=> {url: 'https://foo.bar', query: {foo: 'bar'}, fragmentIdentifier: 'xyz'}
```

#### options

Type: `object`

The options are the same as for `.parse()`.

Extra options are as below.

##### parseFragmentIdentifier

Parse the fragment identifier from the URL.

Type: `boolean`\
Default: `false`

```js
const queryString = require('query-string');

queryString.parseUrl('https://foo.bar?foo=bar#xyz', {parseFragmentIdentifier: true});
//=> {url: 'https://foo.bar', query: {foo: 'bar'}, fragmentIdentifier: 'xyz'}
```

### .stringifyUrl(object, options?)
Expand All @@ -331,12 +356,23 @@ Returns a string with the URL and a query string.

Query items in the `query` property overrides queries in the `url` property.

The `fragmentIdentifier` property overrides the fragment identifier in the `url` property.

```js
queryString.stringifyUrl({url: 'https://foo.bar', query: {foo: 'bar'}});
//=> 'https://foo.bar?foo=bar'

queryString.stringifyUrl({url: 'https://foo.bar?foo=baz', query: {foo: 'bar'}});
//=> 'https://foo.bar?foo=bar'

queryString.stringifyUrl({
url: 'https://foo.bar',
query: {
top: 'foo'
},
fragmentIdentifier: 'bar'
});
//=> 'https://foo.bar?top=foo#bar'
```

#### object
Expand Down
7 changes: 7 additions & 0 deletions test/parse-url.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ test('handles strings with query string that contain =', t => {
t.deepEqual(queryString.parseUrl('https://foo.bar?foo=bar=&foo=baz='), {url: 'https://foo.bar', query: {foo: ['bar=', 'baz=']}});
});

test('handles strings with fragment identifier', t => {
t.deepEqual(queryString.parseUrl('https://foo.bar?top=foo#bar', {parseFragmentIdentifier: true}), {url: 'https://foo.bar', query: {top: 'foo'}, fragmentIdentifier: 'bar'});
t.deepEqual(queryString.parseUrl('https://foo.bar?foo=bar&foo=baz#top', {parseFragmentIdentifier: true}), {url: 'https://foo.bar', query: {foo: ['bar', 'baz']}, fragmentIdentifier: 'top'});
t.deepEqual(queryString.parseUrl('https://foo.bar/#top', {parseFragmentIdentifier: true}), {url: 'https://foo.bar/', query: {}, fragmentIdentifier: 'top'});
t.deepEqual(queryString.parseUrl('https://foo.bar/#st%C3%A5le', {parseFragmentIdentifier: true}), {url: 'https://foo.bar/', query: {}, fragmentIdentifier: 'ståle'});
});

test('throws for invalid values', t => {
t.throws(() => {
queryString.parseUrl(null);
Expand Down
Loading

0 comments on commit 57f4744

Please sign in to comment.