path-parser
Advanced tools
Comparing version 3.0.1 to 4.0.0
@@ -0,1 +1,19 @@ | ||
<a name="4.0.0"></a> | ||
# [4.0.0](https://github.com/troch/path-parser/compare/v3.0.1...v4.0.0) (2018-03-25) | ||
### Features | ||
* make matching case insensitive, add support for 'caseSentive' option ([063aca6](https://github.com/troch/path-parser/commit/063aca6)) | ||
### BREAKING CHANGES | ||
* query parameters can no longer be defined with brackets, instead options are available when matching and building to describe how arrays, booleans and null values are formatted (see 'queryParams' options in README). | ||
* Boolean and null values in query parameters are stringified differently, see the list of options available. To keep behaviour unchanged, set `nullFormat` to `'hidden'` and `booleanFormat` to `'empty-true'`. | ||
* 'trailingSlash' option has been renamed to 'strictTrailingSlash' | ||
* 'test' and 'partialTest' are now case insensitive by default, use 'caseSensitive' option to make it case sensitive | ||
<a name="3.0.1"></a> | ||
@@ -2,0 +20,0 @@ ## [3.0.1](https://github.com/troch/path-parser/compare/v3.0.0...v3.0.1) (2017-11-16) |
@@ -5,78 +5,77 @@ 'use strict'; | ||
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; | ||
/*! ***************************************************************************** | ||
Copyright (c) Microsoft Corporation. All rights reserved. | ||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use | ||
this file except in compliance with the License. You may obtain a copy of the | ||
License at http://www.apache.org/licenses/LICENSE-2.0 | ||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); | ||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED | ||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, | ||
MERCHANTABLITY OR NON-INFRINGEMENT. | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
See the Apache Version 2.0 License for specific language governing permissions | ||
and limitations under the License. | ||
***************************************************************************** */ | ||
var defaultOrConstrained = function defaultOrConstrained(match) { | ||
return '(' + (match ? match.replace(/(^<|>$)/g, '') : '[a-zA-Z0-9-_.~%\':]+') + ')'; | ||
var __assign = Object.assign || function __assign(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
var rules = [{ | ||
// An URL can contain a parameter :paramName | ||
// - and _ are allowed but not in last position | ||
name: 'url-parameter', | ||
pattern: /^:([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})(<(.+?)>)?/, | ||
regex: function regex(match) { | ||
return new RegExp(defaultOrConstrained(match[2])); | ||
var defaultOrConstrained = function (match) { | ||
return '(' + (match ? match.replace(/(^<|>$)/g, '') : "[a-zA-Z0-9-_.~%':]+") + ')'; | ||
}; | ||
var rules = [ | ||
{ | ||
name: 'url-parameter', | ||
pattern: /^:([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})(<(.+?)>)?/, | ||
regex: function (match) { | ||
return new RegExp(defaultOrConstrained(match[2])); | ||
} | ||
}, | ||
{ | ||
name: 'url-parameter-splat', | ||
pattern: /^\*([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})/, | ||
regex: /([^?]*)/ | ||
}, | ||
{ | ||
name: 'url-parameter-matrix', | ||
pattern: /^;([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})(<(.+?)>)?/, | ||
regex: function (match) { | ||
return new RegExp(';' + match[1] + '=' + defaultOrConstrained(match[2])); | ||
} | ||
}, | ||
{ | ||
name: 'query-parameter', | ||
pattern: /^(?:\?|&)(?::)?([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})/ | ||
}, | ||
{ | ||
name: 'delimiter', | ||
pattern: /^(\/|\?)/, | ||
regex: function (match) { return new RegExp('\\' + match[0]); } | ||
}, | ||
{ | ||
name: 'sub-delimiter', | ||
pattern: /^(!|&|-|_|\.|;)/, | ||
regex: function (match) { return new RegExp(match[0]); } | ||
}, | ||
{ | ||
name: 'fragment', | ||
pattern: /^([0-9a-zA-Z]+)/, | ||
regex: function (match) { return new RegExp(match[0]); } | ||
} | ||
}, { | ||
// Url parameter (splat) | ||
name: 'url-parameter-splat', | ||
pattern: /^\*([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})/, | ||
regex: /([^\?]*)/ | ||
}, { | ||
name: 'url-parameter-matrix', | ||
pattern: /^\;([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})(<(.+?)>)?/, | ||
regex: function regex(match) { | ||
return new RegExp(';' + match[1] + '=' + defaultOrConstrained(match[2])); | ||
} | ||
}, { | ||
// Query parameter: ?param1¶m2 | ||
// ?:param1&:param2 | ||
name: 'query-parameter-bracket', | ||
pattern: /^(?:\?|&)(?:\:)?([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})(?:\[\])/ | ||
// regex: match => new RegExp('(?=(\?|.*&)' + match[0] + '(?=(\=|&|$)))') | ||
}, { | ||
// Query parameter: ?param1¶m2 | ||
// ?:param1&:param2 | ||
name: 'query-parameter', | ||
pattern: /^(?:\?|&)(?:\:)?([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})/ | ||
// regex: match => new RegExp('(?=(\?|.*&)' + match[0] + '(?=(\=|&|$)))') | ||
}, { | ||
// Delimiter / | ||
name: 'delimiter', | ||
pattern: /^(\/|\?)/, | ||
regex: function regex(match) { | ||
return new RegExp('\\' + match[0]); | ||
} | ||
}, { | ||
// Sub delimiters | ||
name: 'sub-delimiter', | ||
pattern: /^(\!|\&|\-|_|\.|;)/, | ||
regex: function regex(match) { | ||
return new RegExp(match[0]); | ||
} | ||
}, { | ||
// Unmatched fragment (until delimiter is found) | ||
name: 'fragment', | ||
pattern: /^([0-9a-zA-Z]+)/, | ||
regex: function regex(match) { | ||
return new RegExp(match[0]); | ||
} | ||
}]; | ||
]; | ||
var exists = function exists(val) { | ||
return val !== undefined && val !== null; | ||
}; | ||
var tokenise = function tokenise(str) { | ||
var tokens = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; | ||
var tokenise = function (str, tokens) { | ||
if (tokens === void 0) { tokens = []; } | ||
// Look for a matching rule | ||
var matched = rules.some(function (rule) { | ||
var match = str.match(rule.pattern); | ||
if (!match) return false; | ||
if (!match) { | ||
return false; | ||
} | ||
tokens.push({ | ||
@@ -89,278 +88,212 @@ type: rule.name, | ||
}); | ||
if (match[0].length < str.length) tokens = tokenise(str.substr(match[0].length), tokens); | ||
if (match[0].length < str.length) { | ||
tokens = tokenise(str.substr(match[0].length), tokens); | ||
} | ||
return true; | ||
}); | ||
// If no rules matched, throw an error (possible malformed path) | ||
if (!matched) { | ||
throw new Error('Could not parse path \'' + str + '\''); | ||
throw new Error("Could not parse path '" + str + "'"); | ||
} | ||
// Return tokens | ||
return tokens; | ||
}; | ||
var optTrailingSlash = function optTrailingSlash(source, trailingSlash) { | ||
if (!trailingSlash) return source; | ||
var identity = function (_) { return _; }; | ||
var exists = function (val) { return val !== undefined && val !== null; }; | ||
var optTrailingSlash = function (source, strictTrailingSlash) { | ||
if (!strictTrailingSlash) { | ||
return source; | ||
} | ||
return source.replace(/\\\/$/, '') + '(?:\\/)?'; | ||
}; | ||
var upToDelimiter = function upToDelimiter(source, delimiter) { | ||
if (!delimiter) return source; | ||
return (/(\/)$/.test(source) ? source : source + '(\\/|\\?|\\.|;|$)' | ||
); | ||
var upToDelimiter = function (source, delimiter) { | ||
if (!delimiter) { | ||
return source; | ||
} | ||
return /(\/)$/.test(source) ? source : source + '(\\/|\\?|\\.|;|$)'; | ||
}; | ||
var appendQueryParam = function appendQueryParam(params, param) { | ||
var val = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ''; | ||
if (/\[\]$/.test(param)) { | ||
param = searchParams.withoutBrackets(param); | ||
val = [val]; | ||
var appendQueryParam = function (params, param, val) { | ||
if (val === void 0) { val = ''; } | ||
var existingVal = params[param]; | ||
if (existingVal === undefined) { | ||
params[param] = val; | ||
} | ||
var existingVal = params[param]; | ||
if (existingVal === undefined) params[param] = val;else params[param] = Array.isArray(existingVal) ? existingVal.concat(val) : [existingVal, val]; | ||
else { | ||
params[param] = Array.isArray(existingVal) | ||
? existingVal.concat(val) | ||
: [existingVal, val]; | ||
} | ||
return params; | ||
}; | ||
var parseQueryParams = function parseQueryParams(path) { | ||
var searchPart = searchParams.getSearch(path); | ||
if (!searchPart) return {}; | ||
return searchParams.toObject(searchParams.parse(searchPart)); | ||
}; | ||
function _serialise(key, val) { | ||
if (Array.isArray(val)) { | ||
return val.map(function (v) { | ||
return _serialise(key, v); | ||
}).join('&'); | ||
} | ||
if (val === true) { | ||
return key; | ||
} | ||
return key + '=' + val; | ||
} | ||
var Path = function () { | ||
_createClass(Path, null, [{ | ||
key: 'createPath', | ||
value: function createPath(path) { | ||
return new Path(path); | ||
var Path = /** @class */ (function () { | ||
function Path(path) { | ||
if (!path) { | ||
throw new Error('Missing path in Path constructor'); | ||
} | ||
}, { | ||
key: 'serialise', | ||
value: function serialise(key, val) { | ||
return _serialise(key, val); | ||
} | ||
}]); | ||
function Path(path) { | ||
_classCallCheck(this, Path); | ||
if (!path) throw new Error('Missing path in Path constructor'); | ||
this.path = path; | ||
this.tokens = tokenise(path); | ||
this.hasUrlParams = this.tokens.filter(function (t) { | ||
return (/^url-parameter/.test(t.type) | ||
); | ||
}).length > 0; | ||
this.hasSpatParam = this.tokens.filter(function (t) { | ||
return (/splat$/.test(t.type) | ||
); | ||
}).length > 0; | ||
this.hasMatrixParams = this.tokens.filter(function (t) { | ||
return (/matrix$/.test(t.type) | ||
); | ||
}).length > 0; | ||
this.hasQueryParams = this.tokens.filter(function (t) { | ||
return (/^query-parameter/.test(t.type) | ||
); | ||
}).length > 0; | ||
this.hasUrlParams = | ||
this.tokens.filter(function (t) { return /^url-parameter/.test(t.type); }).length > 0; | ||
this.hasSpatParam = | ||
this.tokens.filter(function (t) { return /splat$/.test(t.type); }).length > 0; | ||
this.hasMatrixParams = | ||
this.tokens.filter(function (t) { return /matrix$/.test(t.type); }).length > 0; | ||
this.hasQueryParams = | ||
this.tokens.filter(function (t) { return /^query-parameter/.test(t.type); }).length > 0; | ||
// Extract named parameters from tokens | ||
this.spatParams = this._getParams('url-parameter-splat'); | ||
this.urlParams = this._getParams(/^url-parameter/); | ||
this.spatParams = this.getParams('url-parameter-splat'); | ||
this.urlParams = this.getParams(/^url-parameter/); | ||
// Query params | ||
this.queryParams = this._getParams('query-parameter'); | ||
this.queryParamsBr = this._getParams('query-parameter-bracket'); | ||
this.queryParams = this.getParams('query-parameter'); | ||
// All params | ||
this.params = this.urlParams.concat(this.queryParams).concat(this.queryParamsBr); | ||
this.params = this.urlParams.concat(this.queryParams); | ||
// Check if hasQueryParams | ||
// Regular expressions for url part only (full and partial match) | ||
this.source = this.tokens.filter(function (t) { | ||
return t.regex !== undefined; | ||
}).map(function (r) { | ||
return r.regex.source; | ||
}).join(''); | ||
this.source = this.tokens | ||
.filter(function (t) { return t.regex !== undefined; }) | ||
.map(function (r) { return r.regex.source; }) | ||
.join(''); | ||
} | ||
_createClass(Path, [{ | ||
key: '_getParams', | ||
value: function _getParams(type) { | ||
var predicate = type instanceof RegExp ? function (t) { | ||
return type.test(t.type); | ||
} : function (t) { | ||
return t.type === type; | ||
}; | ||
return this.tokens.filter(predicate).map(function (t) { | ||
return t.val[0]; | ||
}); | ||
Path.createPath = function (path) { | ||
return new Path(path); | ||
}; | ||
Path.prototype.isQueryParam = function (name) { | ||
return this.queryParams.indexOf(name) !== -1; | ||
}; | ||
Path.prototype.test = function (path, opts) { | ||
var _this = this; | ||
var options = __assign({ strictTrailingSlash: false, queryParams: {} }, opts); | ||
// trailingSlash: falsy => non optional, truthy => optional | ||
var source = optTrailingSlash(this.source, options.strictTrailingSlash); | ||
// Check if exact match | ||
var match = this.urlTest(path, source + (this.hasQueryParams ? '(\\?.*$|$)' : '$'), opts); | ||
// If no match, or no query params, no need to go further | ||
if (!match || !this.hasQueryParams) { | ||
return match; | ||
} | ||
}, { | ||
key: '_isQueryParam', | ||
value: function _isQueryParam(name) { | ||
return this.queryParams.indexOf(name) !== -1 || this.queryParamsBr.indexOf(name) !== -1; | ||
// Extract query params | ||
var queryParams = searchParams.parse(path, options.queryParams); | ||
var unexpectedQueryParams = Object.keys(queryParams).filter(function (p) { return !_this.isQueryParam(p); }); | ||
if (unexpectedQueryParams.length === 0) { | ||
// Extend url match | ||
Object.keys(queryParams).forEach(function (p) { return (match[p] = queryParams[p]); }); | ||
return match; | ||
} | ||
}, { | ||
key: '_urlTest', | ||
value: function _urlTest(path, regex) { | ||
var _this = this; | ||
var match = path.match(regex); | ||
if (!match) return null;else if (!this.urlParams.length) return {}; | ||
// Reduce named params to key-value pairs | ||
return match.slice(1, this.urlParams.length + 1).reduce(function (params, m, i) { | ||
params[_this.urlParams[i]] = decodeURIComponent(m); | ||
return params; | ||
}, {}); | ||
return null; | ||
}; | ||
Path.prototype.partialTest = function (path, opts) { | ||
var _this = this; | ||
var options = __assign({ delimited: true, queryParams: {} }, opts); | ||
// Check if partial match (start of given path matches regex) | ||
// trailingSlash: falsy => non optional, truthy => optional | ||
var source = upToDelimiter(this.source, options.delimited); | ||
var match = this.urlTest(path, source, options); | ||
if (!match) { | ||
return match; | ||
} | ||
}, { | ||
key: 'test', | ||
value: function test(path, opts) { | ||
var _this2 = this; | ||
var options = _extends({ trailingSlash: false }, opts); | ||
// trailingSlash: falsy => non optional, truthy => optional | ||
var source = optTrailingSlash(this.source, options.trailingSlash); | ||
// Check if exact match | ||
var matched = this._urlTest(path, new RegExp('^' + source + (this.hasQueryParams ? '(\\?.*$|$)' : '$'))); | ||
// If no match, or no query params, no need to go further | ||
if (!matched || !this.hasQueryParams) return matched; | ||
// Extract query params | ||
var queryParams = parseQueryParams(path); | ||
var unexpectedQueryParams = Object.keys(queryParams).filter(function (p) { | ||
return _this2.queryParams.concat(_this2.queryParamsBr).indexOf(p) === -1; | ||
}); | ||
if (unexpectedQueryParams.length === 0) { | ||
// Extend url match | ||
Object.keys(queryParams).forEach(function (p) { | ||
return matched[p] = queryParams[p]; | ||
}); | ||
return matched; | ||
} | ||
return null; | ||
} | ||
}, { | ||
key: 'partialTest', | ||
value: function partialTest(path, opts) { | ||
var _this3 = this; | ||
var options = _extends({ delimited: true }, opts); | ||
// Check if partial match (start of given path matches regex) | ||
// trailingSlash: falsy => non optional, truthy => optional | ||
var source = upToDelimiter(this.source, options.delimited); | ||
var match = this._urlTest(path, new RegExp('^' + source)); | ||
if (!match) return match; | ||
if (!this.hasQueryParams) return match; | ||
var queryParams = parseQueryParams(path); | ||
Object.keys(queryParams).filter(function (p) { | ||
return _this3.queryParams.concat(_this3.queryParamsBr).indexOf(p) >= 0; | ||
}).forEach(function (p) { | ||
return appendQueryParam(match, p, queryParams[p]); | ||
}); | ||
if (!this.hasQueryParams) { | ||
return match; | ||
} | ||
}, { | ||
key: 'build', | ||
value: function build() { | ||
var _this4 = this; | ||
var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
var options = _extends({ ignoreConstraints: false, ignoreSearch: false }, opts); | ||
var encodedParams = Object.keys(params).reduce(function (acc, key) { | ||
if (!exists(params[key])) { | ||
return acc; | ||
} | ||
var val = params[key]; | ||
var encode = _this4._isQueryParam(key) ? encodeURIComponent : encodeURI; | ||
if (typeof val === 'boolean') { | ||
acc[key] = val; | ||
} else if (Array.isArray(val)) { | ||
acc[key] = val.map(encode); | ||
} else { | ||
acc[key] = encode(val); | ||
} | ||
var queryParams = searchParams.parse(path, options.queryParams); | ||
Object.keys(queryParams) | ||
.filter(function (p) { return _this.isQueryParam(p); }) | ||
.forEach(function (p) { return appendQueryParam(match, p, queryParams[p]); }); | ||
return match; | ||
}; | ||
Path.prototype.build = function (params, opts) { | ||
var _this = this; | ||
if (params === void 0) { params = {}; } | ||
var options = __assign({ ignoreConstraints: false, ignoreSearch: false, queryParams: {} }, opts); | ||
var encodedUrlParams = Object.keys(params) | ||
.filter(function (p) { return !_this.isQueryParam(p); }) | ||
.reduce(function (acc, key) { | ||
if (!exists(params[key])) { | ||
return acc; | ||
}, {}); | ||
// Check all params are provided (not search parameters which are optional) | ||
if (this.urlParams.some(function (p) { | ||
return !exists(encodedParams[p]); | ||
})) { | ||
var missingParameters = this.urlParams.filter(function (p) { | ||
return !exists(encodedParams[p]); | ||
}); | ||
throw new Error('Cannot build path: \'' + this.path + '\' requires missing parameters { ' + missingParameters.join(', ') + ' }'); | ||
} | ||
// Check constraints | ||
if (!options.ignoreConstraints) { | ||
var constraintsPassed = this.tokens.filter(function (t) { | ||
return (/^url-parameter/.test(t.type) && !/-splat$/.test(t.type) | ||
); | ||
}).every(function (t) { | ||
return new RegExp('^' + defaultOrConstrained(t.otherVal[0]) + '$').test(encodedParams[t.val]); | ||
}); | ||
if (!constraintsPassed) throw new Error('Some parameters of \'' + this.path + '\' are of invalid format'); | ||
var val = params[key]; | ||
var encode = _this.isQueryParam(key) ? identity : encodeURI; | ||
if (typeof val === 'boolean') { | ||
acc[key] = val; | ||
} | ||
var base = this.tokens.filter(function (t) { | ||
return (/^query-parameter/.test(t.type) === false | ||
); | ||
}).map(function (t) { | ||
if (t.type === 'url-parameter-matrix') return ';' + t.val + '=' + encodedParams[t.val[0]]; | ||
return (/^url-parameter/.test(t.type) ? encodedParams[t.val[0]] : t.match | ||
); | ||
}).join(''); | ||
if (options.ignoreSearch) return base; | ||
var queryParams = this.queryParams.concat(this.queryParamsBr.map(function (p) { | ||
return p + '[]'; | ||
})); | ||
var searchPart = queryParams.filter(function (p) { | ||
return Object.keys(encodedParams).indexOf(searchParams.withoutBrackets(p)) !== -1; | ||
}).map(function (p) { | ||
return _serialise(p, encodedParams[searchParams.withoutBrackets(p)]); | ||
}).join('&'); | ||
return base + (searchPart ? '?' + searchPart : ''); | ||
else if (Array.isArray(val)) { | ||
acc[key] = val.map(encode); | ||
} | ||
else { | ||
acc[key] = encode(val); | ||
} | ||
return acc; | ||
}, {}); | ||
// Check all params are provided (not search parameters which are optional) | ||
if (this.urlParams.some(function (p) { return !exists(params[p]); })) { | ||
var missingParameters = this.urlParams.filter(function (p) { return !exists(params[p]); }); | ||
throw new Error("Cannot build path: '" + | ||
this.path + | ||
"' requires missing parameters { " + | ||
missingParameters.join(', ') + | ||
' }'); | ||
} | ||
}]); | ||
// Check constraints | ||
if (!options.ignoreConstraints) { | ||
var constraintsPassed = this.tokens | ||
.filter(function (t) { | ||
return /^url-parameter/.test(t.type) && !/-splat$/.test(t.type); | ||
}) | ||
.every(function (t) { | ||
return new RegExp('^' + defaultOrConstrained(t.otherVal[0]) + '$').test(encodedUrlParams[t.val]); | ||
}); | ||
if (!constraintsPassed) { | ||
throw new Error("Some parameters of '" + this.path + "' are of invalid format"); | ||
} | ||
} | ||
var base = this.tokens | ||
.filter(function (t) { return /^query-parameter/.test(t.type) === false; }) | ||
.map(function (t) { | ||
if (t.type === 'url-parameter-matrix') { | ||
return ";" + t.val + "=" + encodedUrlParams[t.val[0]]; | ||
} | ||
return /^url-parameter/.test(t.type) | ||
? encodedUrlParams[t.val[0]] | ||
: t.match; | ||
}) | ||
.join(''); | ||
if (options.ignoreSearch) { | ||
return base; | ||
} | ||
var searchParams$$1 = this.queryParams | ||
.filter(function (p) { return Object.keys(params).indexOf(p) !== -1; }) | ||
.reduce(function (sparams, paramName) { | ||
sparams[paramName] = params[paramName]; | ||
return sparams; | ||
}, {}); | ||
var searchPart = searchParams.build(searchParams$$1, options.queryParams); | ||
return searchPart ? base + '?' + searchPart : base; | ||
}; | ||
Path.prototype.getParams = function (type) { | ||
var predicate = type instanceof RegExp | ||
? function (t) { return type.test(t.type); } | ||
: function (t) { return t.type === type; }; | ||
return this.tokens.filter(predicate).map(function (t) { return t.val[0]; }); | ||
}; | ||
Path.prototype.urlTest = function (path, source, _a) { | ||
var _this = this; | ||
var _b = (_a === void 0 ? {} : _a).caseSensitive, caseSensitive = _b === void 0 ? false : _b; | ||
var regex = new RegExp('^' + source, caseSensitive ? '' : 'i'); | ||
var match = path.match(regex); | ||
if (!match) { | ||
return null; | ||
} | ||
else if (!this.urlParams.length) { | ||
return {}; | ||
} | ||
// Reduce named params to key-value pairs | ||
return match | ||
.slice(1, this.urlParams.length + 1) | ||
.reduce(function (params, m, i) { | ||
params[_this.urlParams[i]] = decodeURIComponent(m); | ||
return params; | ||
}, {}); | ||
}; | ||
return Path; | ||
}(); | ||
}()); | ||
module.exports = Path; |
@@ -1,79 +0,78 @@ | ||
import { getSearch, parse, toObject, withoutBrackets } from 'search-params'; | ||
import { build, parse } from 'search-params'; | ||
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; | ||
/*! ***************************************************************************** | ||
Copyright (c) Microsoft Corporation. All rights reserved. | ||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use | ||
this file except in compliance with the License. You may obtain a copy of the | ||
License at http://www.apache.org/licenses/LICENSE-2.0 | ||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); | ||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED | ||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, | ||
MERCHANTABLITY OR NON-INFRINGEMENT. | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
See the Apache Version 2.0 License for specific language governing permissions | ||
and limitations under the License. | ||
***************************************************************************** */ | ||
var defaultOrConstrained = function defaultOrConstrained(match) { | ||
return '(' + (match ? match.replace(/(^<|>$)/g, '') : '[a-zA-Z0-9-_.~%\':]+') + ')'; | ||
var __assign = Object.assign || function __assign(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
var rules = [{ | ||
// An URL can contain a parameter :paramName | ||
// - and _ are allowed but not in last position | ||
name: 'url-parameter', | ||
pattern: /^:([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})(<(.+?)>)?/, | ||
regex: function regex(match) { | ||
return new RegExp(defaultOrConstrained(match[2])); | ||
var defaultOrConstrained = function (match) { | ||
return '(' + (match ? match.replace(/(^<|>$)/g, '') : "[a-zA-Z0-9-_.~%':]+") + ')'; | ||
}; | ||
var rules = [ | ||
{ | ||
name: 'url-parameter', | ||
pattern: /^:([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})(<(.+?)>)?/, | ||
regex: function (match) { | ||
return new RegExp(defaultOrConstrained(match[2])); | ||
} | ||
}, | ||
{ | ||
name: 'url-parameter-splat', | ||
pattern: /^\*([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})/, | ||
regex: /([^?]*)/ | ||
}, | ||
{ | ||
name: 'url-parameter-matrix', | ||
pattern: /^;([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})(<(.+?)>)?/, | ||
regex: function (match) { | ||
return new RegExp(';' + match[1] + '=' + defaultOrConstrained(match[2])); | ||
} | ||
}, | ||
{ | ||
name: 'query-parameter', | ||
pattern: /^(?:\?|&)(?::)?([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})/ | ||
}, | ||
{ | ||
name: 'delimiter', | ||
pattern: /^(\/|\?)/, | ||
regex: function (match) { return new RegExp('\\' + match[0]); } | ||
}, | ||
{ | ||
name: 'sub-delimiter', | ||
pattern: /^(!|&|-|_|\.|;)/, | ||
regex: function (match) { return new RegExp(match[0]); } | ||
}, | ||
{ | ||
name: 'fragment', | ||
pattern: /^([0-9a-zA-Z]+)/, | ||
regex: function (match) { return new RegExp(match[0]); } | ||
} | ||
}, { | ||
// Url parameter (splat) | ||
name: 'url-parameter-splat', | ||
pattern: /^\*([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})/, | ||
regex: /([^\?]*)/ | ||
}, { | ||
name: 'url-parameter-matrix', | ||
pattern: /^\;([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})(<(.+?)>)?/, | ||
regex: function regex(match) { | ||
return new RegExp(';' + match[1] + '=' + defaultOrConstrained(match[2])); | ||
} | ||
}, { | ||
// Query parameter: ?param1¶m2 | ||
// ?:param1&:param2 | ||
name: 'query-parameter-bracket', | ||
pattern: /^(?:\?|&)(?:\:)?([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})(?:\[\])/ | ||
// regex: match => new RegExp('(?=(\?|.*&)' + match[0] + '(?=(\=|&|$)))') | ||
}, { | ||
// Query parameter: ?param1¶m2 | ||
// ?:param1&:param2 | ||
name: 'query-parameter', | ||
pattern: /^(?:\?|&)(?:\:)?([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})/ | ||
// regex: match => new RegExp('(?=(\?|.*&)' + match[0] + '(?=(\=|&|$)))') | ||
}, { | ||
// Delimiter / | ||
name: 'delimiter', | ||
pattern: /^(\/|\?)/, | ||
regex: function regex(match) { | ||
return new RegExp('\\' + match[0]); | ||
} | ||
}, { | ||
// Sub delimiters | ||
name: 'sub-delimiter', | ||
pattern: /^(\!|\&|\-|_|\.|;)/, | ||
regex: function regex(match) { | ||
return new RegExp(match[0]); | ||
} | ||
}, { | ||
// Unmatched fragment (until delimiter is found) | ||
name: 'fragment', | ||
pattern: /^([0-9a-zA-Z]+)/, | ||
regex: function regex(match) { | ||
return new RegExp(match[0]); | ||
} | ||
}]; | ||
]; | ||
var exists = function exists(val) { | ||
return val !== undefined && val !== null; | ||
}; | ||
var tokenise = function tokenise(str) { | ||
var tokens = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; | ||
var tokenise = function (str, tokens) { | ||
if (tokens === void 0) { tokens = []; } | ||
// Look for a matching rule | ||
var matched = rules.some(function (rule) { | ||
var match = str.match(rule.pattern); | ||
if (!match) return false; | ||
if (!match) { | ||
return false; | ||
} | ||
tokens.push({ | ||
@@ -86,278 +85,212 @@ type: rule.name, | ||
}); | ||
if (match[0].length < str.length) tokens = tokenise(str.substr(match[0].length), tokens); | ||
if (match[0].length < str.length) { | ||
tokens = tokenise(str.substr(match[0].length), tokens); | ||
} | ||
return true; | ||
}); | ||
// If no rules matched, throw an error (possible malformed path) | ||
if (!matched) { | ||
throw new Error('Could not parse path \'' + str + '\''); | ||
throw new Error("Could not parse path '" + str + "'"); | ||
} | ||
// Return tokens | ||
return tokens; | ||
}; | ||
var optTrailingSlash = function optTrailingSlash(source, trailingSlash) { | ||
if (!trailingSlash) return source; | ||
var identity = function (_) { return _; }; | ||
var exists = function (val) { return val !== undefined && val !== null; }; | ||
var optTrailingSlash = function (source, strictTrailingSlash) { | ||
if (!strictTrailingSlash) { | ||
return source; | ||
} | ||
return source.replace(/\\\/$/, '') + '(?:\\/)?'; | ||
}; | ||
var upToDelimiter = function upToDelimiter(source, delimiter) { | ||
if (!delimiter) return source; | ||
return (/(\/)$/.test(source) ? source : source + '(\\/|\\?|\\.|;|$)' | ||
); | ||
var upToDelimiter = function (source, delimiter) { | ||
if (!delimiter) { | ||
return source; | ||
} | ||
return /(\/)$/.test(source) ? source : source + '(\\/|\\?|\\.|;|$)'; | ||
}; | ||
var appendQueryParam = function appendQueryParam(params, param) { | ||
var val = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ''; | ||
if (/\[\]$/.test(param)) { | ||
param = withoutBrackets(param); | ||
val = [val]; | ||
var appendQueryParam = function (params, param, val) { | ||
if (val === void 0) { val = ''; } | ||
var existingVal = params[param]; | ||
if (existingVal === undefined) { | ||
params[param] = val; | ||
} | ||
var existingVal = params[param]; | ||
if (existingVal === undefined) params[param] = val;else params[param] = Array.isArray(existingVal) ? existingVal.concat(val) : [existingVal, val]; | ||
else { | ||
params[param] = Array.isArray(existingVal) | ||
? existingVal.concat(val) | ||
: [existingVal, val]; | ||
} | ||
return params; | ||
}; | ||
var parseQueryParams = function parseQueryParams(path) { | ||
var searchPart = getSearch(path); | ||
if (!searchPart) return {}; | ||
return toObject(parse(searchPart)); | ||
}; | ||
function _serialise(key, val) { | ||
if (Array.isArray(val)) { | ||
return val.map(function (v) { | ||
return _serialise(key, v); | ||
}).join('&'); | ||
} | ||
if (val === true) { | ||
return key; | ||
} | ||
return key + '=' + val; | ||
} | ||
var Path = function () { | ||
_createClass(Path, null, [{ | ||
key: 'createPath', | ||
value: function createPath(path) { | ||
return new Path(path); | ||
var Path = /** @class */ (function () { | ||
function Path(path) { | ||
if (!path) { | ||
throw new Error('Missing path in Path constructor'); | ||
} | ||
}, { | ||
key: 'serialise', | ||
value: function serialise(key, val) { | ||
return _serialise(key, val); | ||
} | ||
}]); | ||
function Path(path) { | ||
_classCallCheck(this, Path); | ||
if (!path) throw new Error('Missing path in Path constructor'); | ||
this.path = path; | ||
this.tokens = tokenise(path); | ||
this.hasUrlParams = this.tokens.filter(function (t) { | ||
return (/^url-parameter/.test(t.type) | ||
); | ||
}).length > 0; | ||
this.hasSpatParam = this.tokens.filter(function (t) { | ||
return (/splat$/.test(t.type) | ||
); | ||
}).length > 0; | ||
this.hasMatrixParams = this.tokens.filter(function (t) { | ||
return (/matrix$/.test(t.type) | ||
); | ||
}).length > 0; | ||
this.hasQueryParams = this.tokens.filter(function (t) { | ||
return (/^query-parameter/.test(t.type) | ||
); | ||
}).length > 0; | ||
this.hasUrlParams = | ||
this.tokens.filter(function (t) { return /^url-parameter/.test(t.type); }).length > 0; | ||
this.hasSpatParam = | ||
this.tokens.filter(function (t) { return /splat$/.test(t.type); }).length > 0; | ||
this.hasMatrixParams = | ||
this.tokens.filter(function (t) { return /matrix$/.test(t.type); }).length > 0; | ||
this.hasQueryParams = | ||
this.tokens.filter(function (t) { return /^query-parameter/.test(t.type); }).length > 0; | ||
// Extract named parameters from tokens | ||
this.spatParams = this._getParams('url-parameter-splat'); | ||
this.urlParams = this._getParams(/^url-parameter/); | ||
this.spatParams = this.getParams('url-parameter-splat'); | ||
this.urlParams = this.getParams(/^url-parameter/); | ||
// Query params | ||
this.queryParams = this._getParams('query-parameter'); | ||
this.queryParamsBr = this._getParams('query-parameter-bracket'); | ||
this.queryParams = this.getParams('query-parameter'); | ||
// All params | ||
this.params = this.urlParams.concat(this.queryParams).concat(this.queryParamsBr); | ||
this.params = this.urlParams.concat(this.queryParams); | ||
// Check if hasQueryParams | ||
// Regular expressions for url part only (full and partial match) | ||
this.source = this.tokens.filter(function (t) { | ||
return t.regex !== undefined; | ||
}).map(function (r) { | ||
return r.regex.source; | ||
}).join(''); | ||
this.source = this.tokens | ||
.filter(function (t) { return t.regex !== undefined; }) | ||
.map(function (r) { return r.regex.source; }) | ||
.join(''); | ||
} | ||
_createClass(Path, [{ | ||
key: '_getParams', | ||
value: function _getParams(type) { | ||
var predicate = type instanceof RegExp ? function (t) { | ||
return type.test(t.type); | ||
} : function (t) { | ||
return t.type === type; | ||
}; | ||
return this.tokens.filter(predicate).map(function (t) { | ||
return t.val[0]; | ||
}); | ||
Path.createPath = function (path) { | ||
return new Path(path); | ||
}; | ||
Path.prototype.isQueryParam = function (name) { | ||
return this.queryParams.indexOf(name) !== -1; | ||
}; | ||
Path.prototype.test = function (path, opts) { | ||
var _this = this; | ||
var options = __assign({ strictTrailingSlash: false, queryParams: {} }, opts); | ||
// trailingSlash: falsy => non optional, truthy => optional | ||
var source = optTrailingSlash(this.source, options.strictTrailingSlash); | ||
// Check if exact match | ||
var match = this.urlTest(path, source + (this.hasQueryParams ? '(\\?.*$|$)' : '$'), opts); | ||
// If no match, or no query params, no need to go further | ||
if (!match || !this.hasQueryParams) { | ||
return match; | ||
} | ||
}, { | ||
key: '_isQueryParam', | ||
value: function _isQueryParam(name) { | ||
return this.queryParams.indexOf(name) !== -1 || this.queryParamsBr.indexOf(name) !== -1; | ||
// Extract query params | ||
var queryParams = parse(path, options.queryParams); | ||
var unexpectedQueryParams = Object.keys(queryParams).filter(function (p) { return !_this.isQueryParam(p); }); | ||
if (unexpectedQueryParams.length === 0) { | ||
// Extend url match | ||
Object.keys(queryParams).forEach(function (p) { return (match[p] = queryParams[p]); }); | ||
return match; | ||
} | ||
}, { | ||
key: '_urlTest', | ||
value: function _urlTest(path, regex) { | ||
var _this = this; | ||
var match = path.match(regex); | ||
if (!match) return null;else if (!this.urlParams.length) return {}; | ||
// Reduce named params to key-value pairs | ||
return match.slice(1, this.urlParams.length + 1).reduce(function (params, m, i) { | ||
params[_this.urlParams[i]] = decodeURIComponent(m); | ||
return params; | ||
}, {}); | ||
return null; | ||
}; | ||
Path.prototype.partialTest = function (path, opts) { | ||
var _this = this; | ||
var options = __assign({ delimited: true, queryParams: {} }, opts); | ||
// Check if partial match (start of given path matches regex) | ||
// trailingSlash: falsy => non optional, truthy => optional | ||
var source = upToDelimiter(this.source, options.delimited); | ||
var match = this.urlTest(path, source, options); | ||
if (!match) { | ||
return match; | ||
} | ||
}, { | ||
key: 'test', | ||
value: function test(path, opts) { | ||
var _this2 = this; | ||
var options = _extends({ trailingSlash: false }, opts); | ||
// trailingSlash: falsy => non optional, truthy => optional | ||
var source = optTrailingSlash(this.source, options.trailingSlash); | ||
// Check if exact match | ||
var matched = this._urlTest(path, new RegExp('^' + source + (this.hasQueryParams ? '(\\?.*$|$)' : '$'))); | ||
// If no match, or no query params, no need to go further | ||
if (!matched || !this.hasQueryParams) return matched; | ||
// Extract query params | ||
var queryParams = parseQueryParams(path); | ||
var unexpectedQueryParams = Object.keys(queryParams).filter(function (p) { | ||
return _this2.queryParams.concat(_this2.queryParamsBr).indexOf(p) === -1; | ||
}); | ||
if (unexpectedQueryParams.length === 0) { | ||
// Extend url match | ||
Object.keys(queryParams).forEach(function (p) { | ||
return matched[p] = queryParams[p]; | ||
}); | ||
return matched; | ||
} | ||
return null; | ||
} | ||
}, { | ||
key: 'partialTest', | ||
value: function partialTest(path, opts) { | ||
var _this3 = this; | ||
var options = _extends({ delimited: true }, opts); | ||
// Check if partial match (start of given path matches regex) | ||
// trailingSlash: falsy => non optional, truthy => optional | ||
var source = upToDelimiter(this.source, options.delimited); | ||
var match = this._urlTest(path, new RegExp('^' + source)); | ||
if (!match) return match; | ||
if (!this.hasQueryParams) return match; | ||
var queryParams = parseQueryParams(path); | ||
Object.keys(queryParams).filter(function (p) { | ||
return _this3.queryParams.concat(_this3.queryParamsBr).indexOf(p) >= 0; | ||
}).forEach(function (p) { | ||
return appendQueryParam(match, p, queryParams[p]); | ||
}); | ||
if (!this.hasQueryParams) { | ||
return match; | ||
} | ||
}, { | ||
key: 'build', | ||
value: function build() { | ||
var _this4 = this; | ||
var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
var options = _extends({ ignoreConstraints: false, ignoreSearch: false }, opts); | ||
var encodedParams = Object.keys(params).reduce(function (acc, key) { | ||
if (!exists(params[key])) { | ||
return acc; | ||
} | ||
var val = params[key]; | ||
var encode = _this4._isQueryParam(key) ? encodeURIComponent : encodeURI; | ||
if (typeof val === 'boolean') { | ||
acc[key] = val; | ||
} else if (Array.isArray(val)) { | ||
acc[key] = val.map(encode); | ||
} else { | ||
acc[key] = encode(val); | ||
} | ||
var queryParams = parse(path, options.queryParams); | ||
Object.keys(queryParams) | ||
.filter(function (p) { return _this.isQueryParam(p); }) | ||
.forEach(function (p) { return appendQueryParam(match, p, queryParams[p]); }); | ||
return match; | ||
}; | ||
Path.prototype.build = function (params, opts) { | ||
var _this = this; | ||
if (params === void 0) { params = {}; } | ||
var options = __assign({ ignoreConstraints: false, ignoreSearch: false, queryParams: {} }, opts); | ||
var encodedUrlParams = Object.keys(params) | ||
.filter(function (p) { return !_this.isQueryParam(p); }) | ||
.reduce(function (acc, key) { | ||
if (!exists(params[key])) { | ||
return acc; | ||
}, {}); | ||
// Check all params are provided (not search parameters which are optional) | ||
if (this.urlParams.some(function (p) { | ||
return !exists(encodedParams[p]); | ||
})) { | ||
var missingParameters = this.urlParams.filter(function (p) { | ||
return !exists(encodedParams[p]); | ||
}); | ||
throw new Error('Cannot build path: \'' + this.path + '\' requires missing parameters { ' + missingParameters.join(', ') + ' }'); | ||
} | ||
// Check constraints | ||
if (!options.ignoreConstraints) { | ||
var constraintsPassed = this.tokens.filter(function (t) { | ||
return (/^url-parameter/.test(t.type) && !/-splat$/.test(t.type) | ||
); | ||
}).every(function (t) { | ||
return new RegExp('^' + defaultOrConstrained(t.otherVal[0]) + '$').test(encodedParams[t.val]); | ||
}); | ||
if (!constraintsPassed) throw new Error('Some parameters of \'' + this.path + '\' are of invalid format'); | ||
var val = params[key]; | ||
var encode = _this.isQueryParam(key) ? identity : encodeURI; | ||
if (typeof val === 'boolean') { | ||
acc[key] = val; | ||
} | ||
var base = this.tokens.filter(function (t) { | ||
return (/^query-parameter/.test(t.type) === false | ||
); | ||
}).map(function (t) { | ||
if (t.type === 'url-parameter-matrix') return ';' + t.val + '=' + encodedParams[t.val[0]]; | ||
return (/^url-parameter/.test(t.type) ? encodedParams[t.val[0]] : t.match | ||
); | ||
}).join(''); | ||
if (options.ignoreSearch) return base; | ||
var queryParams = this.queryParams.concat(this.queryParamsBr.map(function (p) { | ||
return p + '[]'; | ||
})); | ||
var searchPart = queryParams.filter(function (p) { | ||
return Object.keys(encodedParams).indexOf(withoutBrackets(p)) !== -1; | ||
}).map(function (p) { | ||
return _serialise(p, encodedParams[withoutBrackets(p)]); | ||
}).join('&'); | ||
return base + (searchPart ? '?' + searchPart : ''); | ||
else if (Array.isArray(val)) { | ||
acc[key] = val.map(encode); | ||
} | ||
else { | ||
acc[key] = encode(val); | ||
} | ||
return acc; | ||
}, {}); | ||
// Check all params are provided (not search parameters which are optional) | ||
if (this.urlParams.some(function (p) { return !exists(params[p]); })) { | ||
var missingParameters = this.urlParams.filter(function (p) { return !exists(params[p]); }); | ||
throw new Error("Cannot build path: '" + | ||
this.path + | ||
"' requires missing parameters { " + | ||
missingParameters.join(', ') + | ||
' }'); | ||
} | ||
}]); | ||
// Check constraints | ||
if (!options.ignoreConstraints) { | ||
var constraintsPassed = this.tokens | ||
.filter(function (t) { | ||
return /^url-parameter/.test(t.type) && !/-splat$/.test(t.type); | ||
}) | ||
.every(function (t) { | ||
return new RegExp('^' + defaultOrConstrained(t.otherVal[0]) + '$').test(encodedUrlParams[t.val]); | ||
}); | ||
if (!constraintsPassed) { | ||
throw new Error("Some parameters of '" + this.path + "' are of invalid format"); | ||
} | ||
} | ||
var base = this.tokens | ||
.filter(function (t) { return /^query-parameter/.test(t.type) === false; }) | ||
.map(function (t) { | ||
if (t.type === 'url-parameter-matrix') { | ||
return ";" + t.val + "=" + encodedUrlParams[t.val[0]]; | ||
} | ||
return /^url-parameter/.test(t.type) | ||
? encodedUrlParams[t.val[0]] | ||
: t.match; | ||
}) | ||
.join(''); | ||
if (options.ignoreSearch) { | ||
return base; | ||
} | ||
var searchParams = this.queryParams | ||
.filter(function (p) { return Object.keys(params).indexOf(p) !== -1; }) | ||
.reduce(function (sparams, paramName) { | ||
sparams[paramName] = params[paramName]; | ||
return sparams; | ||
}, {}); | ||
var searchPart = build(searchParams, options.queryParams); | ||
return searchPart ? base + '?' + searchPart : base; | ||
}; | ||
Path.prototype.getParams = function (type) { | ||
var predicate = type instanceof RegExp | ||
? function (t) { return type.test(t.type); } | ||
: function (t) { return t.type === type; }; | ||
return this.tokens.filter(predicate).map(function (t) { return t.val[0]; }); | ||
}; | ||
Path.prototype.urlTest = function (path, source, _a) { | ||
var _this = this; | ||
var _b = (_a === void 0 ? {} : _a).caseSensitive, caseSensitive = _b === void 0 ? false : _b; | ||
var regex = new RegExp('^' + source, caseSensitive ? '' : 'i'); | ||
var match = path.match(regex); | ||
if (!match) { | ||
return null; | ||
} | ||
else if (!this.urlParams.length) { | ||
return {}; | ||
} | ||
// Reduce named params to key-value pairs | ||
return match | ||
.slice(1, this.urlParams.length + 1) | ||
.reduce(function (params, m, i) { | ||
params[_this.urlParams[i]] = decodeURIComponent(m); | ||
return params; | ||
}, {}); | ||
}; | ||
return Path; | ||
}(); | ||
}()); | ||
export default Path; |
120
package.json
{ | ||
"name": "path-parser", | ||
"version": "3.0.1", | ||
"description": "A small utility to parse, match and generate paths", | ||
"main": "dist/cjs/path-parser.js", | ||
"jsnext:main": "dist/es/path-parser.js", | ||
"module": "dist/es/path-parser.js", | ||
"scripts": { | ||
"test": "mocha --compilers js:babel-core/register", | ||
"test-cover": "babel-node node_modules/.bin/isparta cover node_modules/.bin/_mocha", | ||
"lint": "eslint modules/*.js", | ||
"build": "npm run clean && rollup -c rollup.config.js", | ||
"clean": "rimraf dist", | ||
"clog": "conventional-changelog -p angular -i CHANGELOG.md -s" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/troch/path-parser.git" | ||
}, | ||
"keywords": [ | ||
"path", | ||
"parser", | ||
"url", | ||
"route", | ||
"match", | ||
"partial match", | ||
"regex" | ||
], | ||
"author": "Thomas Roch", | ||
"license": "ISC", | ||
"bugs": { | ||
"url": "https://github.com/troch/path-parser/issues" | ||
}, | ||
"homepage": "https://github.com/troch/path-parser", | ||
"devDependencies": { | ||
"babel-core": "~6.26.0", | ||
"babel-eslint": "~8.0.2", | ||
"babel-plugin-add-module-exports": "~0.2.1", | ||
"babel-plugin-transform-class-properties": "~6.24.1", | ||
"babel-plugin-transform-export-extensions": "~6.22.0", | ||
"babel-plugin-transform-object-rest-spread": "~6.26.0", | ||
"babel-preset-env": "~1.6.1", | ||
"conventional-changelog": "~1.1.7", | ||
"coveralls": "~3.0.0", | ||
"eslint": "~4.11.0", | ||
"isparta": "~4.0.0", | ||
"mkdirp": "~0.5.1", | ||
"mocha": "~4.0.1", | ||
"rimraf": "~2.6.2", | ||
"rollup": "~0.51.5", | ||
"rollup-plugin-babel": "~3.0.2", | ||
"should": "~13.1.3" | ||
}, | ||
"dependencies": { | ||
"search-params": "~1.3.0" | ||
} | ||
"name": "path-parser", | ||
"version": "4.0.0", | ||
"description": "A small utility to parse, match and generate paths", | ||
"main": "dist/cjs/path-parser.js", | ||
"jsnext:main": "dist/es/path-parser.js", | ||
"module": "dist/es/path-parser.js", | ||
"side-effects": false, | ||
"typings": "./typings/index.d.ts", | ||
"scripts": { | ||
"test": "mocha -r ts-node/register 'test/main.js'", | ||
"lint": "tslint modules/*.ts", | ||
"build": "npm run clean && rollup -c rollup.config.js", | ||
"clean": "rimraf dist && rimraf temp", | ||
"clog": "conventional-changelog -p angular -i CHANGELOG.md -s", | ||
"precommit": "lint-staged" | ||
}, | ||
"lint-staged": { | ||
"modules/*.ts": [ | ||
"prettier --write", | ||
"tslint", | ||
"git add" | ||
], | ||
"test/*.js": [ | ||
"prettier --write", | ||
"git add" | ||
] | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/troch/path-parser.git" | ||
}, | ||
"keywords": [ | ||
"path", | ||
"parser", | ||
"url", | ||
"route", | ||
"match", | ||
"partial match", | ||
"regex" | ||
], | ||
"author": "Thomas Roch", | ||
"license": "ISC", | ||
"bugs": { | ||
"url": "https://github.com/troch/path-parser/issues" | ||
}, | ||
"homepage": "https://github.com/troch/path-parser", | ||
"devDependencies": { | ||
"conventional-changelog": "~1.1.18", | ||
"husky": "~0.14.3", | ||
"lint-staged": "~7.0.0", | ||
"mkdirp": "~0.5.1", | ||
"mocha": "~5.0.4", | ||
"prettier": "~1.11.1", | ||
"rimraf": "~2.6.2", | ||
"rollup": "~0.57.1", | ||
"rollup-plugin-typescript2": "~0.12.0", | ||
"should": "~13.2.1", | ||
"ts-node": "~5.0.1", | ||
"tslint": "~5.9.1", | ||
"tslint-config-prettier": "~1.10.0", | ||
"typescript": "~2.7.2" | ||
}, | ||
"dependencies": { | ||
"search-params": "2.1.1" | ||
} | ||
} |
101
README.md
[![npm version](https://badge.fury.io/js/path-parser.svg)](http://badge.fury.io/js/path-parser) | ||
[![Build Status](https://travis-ci.org/troch/path-parser.svg)](https://travis-ci.org/troch/path-parser) | ||
[![Coverage Status](https://coveralls.io/repos/troch/path-parser/badge.svg?branch=master)](https://coveralls.io/r/troch/path-parser?branch=master) | ||
# path-parser | ||
A small utility to parse and build paths. It can be used to partially or fully | ||
A small library to parse and build paths. It can be used to partially or fully | ||
test paths against a defined pattern. | ||
@@ -13,16 +12,26 @@ | ||
## Usage | ||
```javascript | ||
import Path from 'path-parser'; | ||
// Defining a new path | ||
const p = new Path('/users/profile/:id'); | ||
import Path from 'path-parser' | ||
const path = new Path('/users/:id') | ||
// Matching | ||
p.test('/users/profile/00123') // => {id: "00123"} | ||
// Partial testing: does this path | ||
// starts with that pattern? | ||
p.partialTest('/users/profile/00123/orders') // => {id: "00123"} | ||
p.partialTest('/profile/00123/orders') // => null | ||
path.test('/users/00123') | ||
// { | ||
// id: "00123" | ||
// } | ||
// Partial testing: does the provided path | ||
// starts with the defined pattern? | ||
path.partialTest('/users/00123/orders') | ||
// { | ||
// id: "00123" | ||
// } | ||
path.partialTest('/profile/00123/orders') | ||
// null | ||
// Building | ||
p.build({id: '00123'}) // => "users/profile/00123" | ||
path.build({ id: '00123' }) | ||
// => "/users/00123" | ||
``` | ||
@@ -33,5 +42,3 @@ | ||
```javascript | ||
import Path from 'path-parser'; | ||
const p = Path.createPath('/users/profile/:id'); | ||
const path = Path.createPath('/users/:id') | ||
``` | ||
@@ -41,10 +48,8 @@ | ||
- `:param`: for URL parameters | ||
- `;param`: for matrix parameters | ||
- `*splat`: for parameters spanning over multiple segments. Handle with care | ||
- `?param1¶m2` or `?:param1&:param2`: for query parameters. Colons `:` are optional. | ||
- `?param1=a¶m1=b` will result in `{param1: ['a', 'b']}` | ||
- `?param1[]=a` and `?param1[]=a¶m1[]=b` will result respectively in `{param1: ['a']}` and `{param1: ['a', 'b']}` | ||
* `:param`: for URL parameters | ||
* `;param`: for matrix parameters | ||
* `*splat`: for parameters spanning over multiple segments. Handle with care | ||
* `?param1¶m2` or `?:param1&:param2`: for query parameters. Colons `:` are optional. | ||
## Parameter constraints | ||
#### Parameter constraints | ||
@@ -54,4 +59,4 @@ For URL parameters and matrix parameters, you can add a constraint in the form of a regular expression. | ||
- `:param<\\d+>` will match numbers only for parameter `param` | ||
- `;id<[a-fA-F0-9]{8}` will match 8 characters hexadecimal strings for parameter `id` | ||
* `:param<\\d+>` will match numbers only for parameter `param` | ||
* `;id<[a-fA-F0-9]{8}` will match 8 characters hexadecimal strings for parameter `id` | ||
@@ -62,36 +67,40 @@ Constraints are also applied when building paths, unless specified otherwise (set option flag `ignoreConstraints` to true). | ||
// Path.build(params, opts) | ||
var Path = new Path('/users/profile/:id<\d+>'); | ||
var Path = new Path('/users/:id<d+>') | ||
path.build({id: 'not-a-number'}); // => Will throw an error | ||
path.build({id: 'not-a-number'}, {ignoreConstraints: true}); // => '/users/profile/not-a-number' | ||
path.build({ id: 'not-a-number' }) // => Will throw an error | ||
path.build({ id: '123' }) // => '/users/123' | ||
``` | ||
## Optional trailing slashes | ||
## API | ||
`.test(path, options)` accepts an option object: | ||
- `trailingSlash`: if truthy, it will make trailing slashes optional (default to `true`). | ||
```javascript | ||
var path = new Path('/my-path'); | ||
### path.test(path: string, opts?: object): object | null; | ||
path.test('/my-path/') // => null | ||
path.test('/my-path/', { trailingSlash: true }) // => {} | ||
``` | ||
Test if the provided path matches the defined path template. Options available are: | ||
- `'caseSensitive'`: whether matching should be case sensitive or not (default to `false`) | ||
- `'strictTrailingSlash'`: whether or not it should strictly match trailing slashes (default to `false`) | ||
- `'queryParams'`: [options for query parameters](https://github.com/troch/search-params#options) | ||
## Partial test with delimiters | ||
`.partialTest(path, options)` accepts an option object: | ||
- `delimited`: if truthy, a partial test will only be successful if a delimiter is found at the end of a match (default to `true`, delimiters are `/`, `?`, `.` and `;`). | ||
### path.partialTest(path: string, opts?: object): object | null; | ||
```javascript | ||
var path = new Path('/my-path'); | ||
Test if the provided path is partially matched (starts with) the defined path template. Options available are: | ||
- `'caseSensitive'`: whether matching should be case sensitive or not (default to `false`) | ||
- `'delimited'`: whether or not a partial match should only be successful if it reaches a delimiter (`/`, `?`, `.` and `;`). Default to `true`. | ||
- `'queryParams'`: [options for query parameters](https://github.com/troch/search-params#options) | ||
path.partialTest('/my-path/extended') // => {} | ||
path.partialTest('/my-path-extended') // => null | ||
path.partialTest('/my-path-extended', { delimited: false }) // => {} | ||
``` | ||
### path.build(params?: object, opts?: object): string; | ||
Builds the defined path template with the provided parameters | ||
- `'ignoreConstraints'`: whether or not to ignore parameter constraints (default to `false`) | ||
- `'ignoreSearch'`: whether or not to build query parameters (default to `false`) | ||
- `'caseSensitive'`: whether matching should be case sensitive or not (default to `false`) | ||
- `'queryParams'`: [options for query parameters](https://github.com/troch/search-params#options) | ||
## Related modules | ||
- [route-parser](https://github.com/rcs/route-parser) | ||
- [url-pattern](https://github.com/snd/url-pattern) | ||
* [route-parser](https://github.com/rcs/route-parser) | ||
* [url-pattern](https://github.com/snd/url-pattern) |
Sorry, the diff of this file is not supported yet
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
150435
14
10
102
632
1
+ Addedsearch-params@2.1.1(transitive)
- Removedsearch-params@1.3.0(transitive)
Updatedsearch-params@2.1.1