url-pattern
Advanced tools
Comparing version 0.7.1 to 0.8.0
@@ -1,4 +0,2 @@ | ||
// Generated by CoffeeScript 1.9.1 | ||
var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; | ||
// Generated by CoffeeScript 1.9.2 | ||
(function(root, factory) { | ||
@@ -13,4 +11,4 @@ if (('function' === typeof define) && (define.amd != null)) { | ||
})(this, function() { | ||
var UrlPattern; | ||
UrlPattern = function(arg, separator) { | ||
var UrlPattern, alphanumericRegex; | ||
UrlPattern = function(arg) { | ||
if (arg instanceof UrlPattern) { | ||
@@ -26,12 +24,6 @@ this.isRegex = arg.isRegex; | ||
} | ||
[':', '*'].forEach(function(forbidden) { | ||
if (separator === forbidden) { | ||
throw new Error("separator can't be " + forbidden); | ||
} | ||
}); | ||
if (this.isRegex) { | ||
this.regex = arg; | ||
} else { | ||
this.regex = new RegExp(this.toRegexString(arg, separator)); | ||
this.names = this.getNames(arg, separator); | ||
this.compile(arg); | ||
} | ||
@@ -57,7 +49,7 @@ return this; | ||
} | ||
if (name === '_') { | ||
if (bound._ == null) { | ||
bound._ = []; | ||
if (bound[name] != null) { | ||
if (!Array.isArray(bound[name])) { | ||
bound[name] = [bound[name]]; | ||
} | ||
bound._.push(value); | ||
bound[name].push(value); | ||
} else { | ||
@@ -69,52 +61,83 @@ bound[name] = value; | ||
}; | ||
alphanumericRegex = new RegExp('^[a-zA-Z0-9]+$'); | ||
UrlPattern.prototype.isAlphanumeric = function(string) { | ||
return alphanumericRegex.test(string); | ||
}; | ||
UrlPattern.prototype.escapeForRegex = function(string) { | ||
return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); | ||
}; | ||
UrlPattern.prototype.getNames = function(arg, separator) { | ||
var escapedSeparator, name, names, regex, results; | ||
if (separator == null) { | ||
separator = '/'; | ||
} | ||
if (arg instanceof RegExp) { | ||
return []; | ||
} | ||
escapedSeparator = this.escapeForRegex(separator); | ||
regex = new RegExp("((:?:[^" + escapedSeparator + "\(\)]+)|(?:[\*]))", 'g'); | ||
UrlPattern.prototype.compile = function(string) { | ||
var char, enter, index, leave, length, mode, names, openParens, regexString, sliceBegin, that; | ||
names = []; | ||
results = regex.exec(arg); | ||
while (results != null) { | ||
name = results[1].slice(1); | ||
if (name === '_') { | ||
throw new TypeError(":_ can't be used as a pattern name in pattern " + arg); | ||
regexString = '^'; | ||
mode = '?'; | ||
sliceBegin = 0; | ||
openParens = 0; | ||
that = this; | ||
index = -1; | ||
leave = function() { | ||
switch (mode) { | ||
case 'variable': | ||
if ((index - sliceBegin) < 2) { | ||
throw new Error("`:` must be followed by at least one alphanumeric character that is the variable name at " + index); | ||
} | ||
names.push(string.slice(sliceBegin + 1, index)); | ||
regexString += "([a-zA-Z0-9]+)"; | ||
break; | ||
case 'static': | ||
regexString += that.escapeForRegex(string.slice(sliceBegin, index)); | ||
} | ||
if (indexOf.call(names, name) >= 0) { | ||
throw new TypeError("duplicate pattern name :" + name + " in pattern " + arg); | ||
return mode = '?'; | ||
}; | ||
enter = function(nextMode) { | ||
if (nextMode === mode) { | ||
return; | ||
} | ||
names.push(name || '_'); | ||
results = regex.exec(arg); | ||
leave(); | ||
sliceBegin = index; | ||
return mode = nextMode; | ||
}; | ||
length = string.length; | ||
while (++index < length) { | ||
char = string.charAt(index); | ||
if (char === ':') { | ||
if (mode === 'variable') { | ||
throw new Error("cannot start variable right after variable at " + index); | ||
} | ||
enter('variable'); | ||
} else if (char === '(') { | ||
leave(); | ||
openParens++; | ||
regexString += '(?:'; | ||
} else if (char === ')') { | ||
leave(); | ||
openParens--; | ||
if (openParens < 0) { | ||
throw new Error("did not expect ) at " + index); | ||
} | ||
regexString += ')?'; | ||
} else if (char === '*') { | ||
leave(); | ||
regexString += '(.*?)'; | ||
names.push('_'); | ||
} else { | ||
switch (mode) { | ||
case 'variable': | ||
if (!this.isAlphanumeric(char)) { | ||
enter('static'); | ||
} | ||
break; | ||
case '?': | ||
enter('static'); | ||
} | ||
} | ||
} | ||
return names; | ||
}; | ||
UrlPattern.prototype.escapeSeparators = function(string, separator) { | ||
var escapedSeparator, regex; | ||
if (separator == null) { | ||
separator = '/'; | ||
if (openParens > 0) { | ||
throw new Error("unclosed parentheses at " + index); | ||
} | ||
escapedSeparator = UrlPattern.prototype.escapeForRegex(separator); | ||
regex = new RegExp(escapedSeparator, 'g'); | ||
return string.replace(regex, escapedSeparator); | ||
leave(); | ||
regexString += '$'; | ||
this.names = names; | ||
return this.regex = new RegExp(regexString); | ||
}; | ||
UrlPattern.prototype.toRegexString = function(string, separator) { | ||
var escapedSeparator, stringWithEscapedSeparators; | ||
if (separator == null) { | ||
separator = '/'; | ||
} | ||
stringWithEscapedSeparators = UrlPattern.prototype.escapeSeparators(string, separator); | ||
stringWithEscapedSeparators = stringWithEscapedSeparators.replace(/\((.*?)\)/g, '(?:$1)?').replace(/\*/g, '(.*?)'); | ||
escapedSeparator = UrlPattern.prototype.escapeForRegex(separator); | ||
UrlPattern.prototype.getNames(string, separator).forEach(function(name) { | ||
return stringWithEscapedSeparators = stringWithEscapedSeparators.replace(':' + name, "([^\\" + separator + "]+)"); | ||
}); | ||
return "^" + stringWithEscapedSeparators + "$"; | ||
}; | ||
UrlPattern.newPattern = function() { | ||
@@ -121,0 +144,0 @@ throw Error('`urlPattern.newPattern` is no longer supported. Use `new Pattern` instead.'); |
{ | ||
"name": "url-pattern", | ||
"version": "0.7.1", | ||
"description": "url-pattern is easy pattern matching and segment extraction for urls, domains, filepaths and any string composed of segments joined by a separator character", | ||
"keywords": ["url", "pattern", "matching", "routing", "route", "regex", "match", "segment", "path", "domain", "separator"], | ||
"version": "0.8.0", | ||
"description": "url-pattern is simple pattern matching and segment extraction for urls, domains, filepaths and other strings", | ||
"keywords": ["url", "pattern", "matching", "routing", "route", "regex", "match", "segment", "parsing", "parser", "path", "domain", "separator"], | ||
"homepage": "http://github.com/snd/url-pattern", | ||
@@ -46,3 +46,3 @@ "author": { | ||
"nodeunit": "0.9.1", | ||
"coffee-script": "1.9.1" | ||
"coffee-script": "1.9.2" | ||
}, | ||
@@ -49,0 +49,0 @@ "main": "lib/url-pattern", |
217
README.md
@@ -7,7 +7,7 @@ # url-pattern | ||
> url-pattern is easy pattern matching and segment extraction for | ||
> urls, domains, filepaths and any string composed of segments joined | ||
> by a separator character | ||
**url-pattern is simple pattern matching and segment extraction for | ||
urls, domains, filepaths and other strings** | ||
[check out **passage** if you are looking for simple composable routing that builds on top of url-pattern](https://github.com/snd/passage) | ||
> This is a great little library -- thanks! | ||
> <small>[michael](https://github.com/snd/url-pattern/pull/7)</small> | ||
@@ -18,130 +18,130 @@ ``` | ||
require with commonjs: | ||
``` javascript | ||
> var UrlPattern = require('url-pattern'); | ||
``` | ||
``` javascript | ||
> var pattern = new UrlPattern('/api/users/:id'); | ||
```javascript | ||
var Pattern = require('url-pattern'); | ||
> pattern.match('/api/users/10'); | ||
{id: '10'} | ||
> pattern.match('/api/products/5'); | ||
null | ||
``` | ||
``` javascript | ||
> var pattern = new UrlPattern('/v:major(.:minor)/*'); | ||
[lib/url-pattern.js](lib/url-pattern.js) can be used in the browser. | ||
it supports AMD as well. | ||
> pattern.match('/v1.2/'); | ||
{major: '1', minor: '2', _: ''} | ||
### match urls or filepaths | ||
> pattern.match('/v2/users'); | ||
{major: '2', _: 'users'} | ||
##### make pattern from string | ||
```javascript | ||
var pattern = new Pattern('/users/:id'); | ||
> pattern.match('/v/'); | ||
null | ||
``` | ||
the default separator is `/`. you can pass a custom separator | ||
as the second argument. | ||
[lib/url-pattern.js](lib/url-pattern.js) supports [AMD](http://requirejs.org/docs/whyamd.html). | ||
if [AMD](http://requirejs.org/docs/whyamd.html) is not available it sets the global variable `UrlPattern`. | ||
##### match pattern against url | ||
[check out **passage** if you are looking for simple composable routing that builds on top of url-pattern](https://github.com/snd/passage) | ||
match returns the extracted parameters or `null` if there was no match: | ||
### make pattern from string | ||
```javascript | ||
pattern.match('/users/5'); // => {id: '5'} | ||
pattern.match('/projects/5'); // => null | ||
> var pattern = new UrlPattern('/users/:id'); | ||
``` | ||
##### make pattern from regex | ||
### match pattern against string | ||
match returns the extracted segments: | ||
```javascript | ||
var regexPattern = new Pattern(/\/test\/(.*)/); | ||
> pattern.match('/users/5'); | ||
{id: '5'} | ||
``` | ||
##### match regex pattern against url | ||
or `null` if there was no match: | ||
if the pattern was created from a regex an array of the captured groups is returned on match: | ||
```javascript | ||
regexPattern.match('/test/users'); // => ['users'] | ||
regexPattern.match('/users/test'); // => null | ||
``` javascript | ||
> pattern.match('/projects/5'); | ||
null | ||
``` | ||
##### make wildcard pattern from string | ||
named segment names (starting with `:`) and named segment values | ||
stop at the next non-alphanumeric character. | ||
### make pattern from regex | ||
```javascript | ||
var wildcardPattern = new Pattern('*/users/:id/*'); | ||
> var pattern = new UrlPattern(/\/test\/(.*)/); | ||
``` | ||
##### match wildcard pattern against url | ||
### match regex pattern against string | ||
wildcard matches are collected in the `_` property: | ||
if the pattern was created from a regex an array of the captured groups is returned on a match: | ||
```javascript | ||
wildcardPattern.match('/api/v1/users/10/followers/20'); | ||
// => {id: '10', _: ['/api/v1', 'followers/20']} | ||
> pattern.match('/test/users'); | ||
['users'] | ||
> pattern.match('/users/test'); | ||
null | ||
``` | ||
##### make optional pattern from string | ||
### wildcards | ||
```javascript | ||
var optionalPattern = new Pattern('(/)users(/:foo)/bar(/*)'); | ||
var pattern = new Pattern('*/users/:id/*'); | ||
``` | ||
##### match optional pattern against url | ||
wildcard matches are collected in the `_` property: | ||
optional matches are stored in the corresponding property, if they exist. | ||
```javascript | ||
optionalPattern.match('users/bar'); | ||
// => {} | ||
optionalPattern.match('/users/bar'); | ||
// => {} | ||
optionalPattern.match('/users/biff/bar'); | ||
// => {foo: 'biff'} | ||
optionalPattern.match('/users/biff/bar/beep/boop'); | ||
// => {foo: 'biff', _: ['beep/boop']} | ||
> pattern.match('/api/v1/users/10/followers/20'); | ||
{id: '10', _: ['/api/v1', 'followers/20']} | ||
``` | ||
### match domains | ||
if there is only one wildcard `_` contains the matching string. | ||
otherwise `_` contains an array of matching strings. | ||
##### make pattern from string | ||
### optional segments | ||
```javascript | ||
var pattern = new Pattern(':sub.google.com', '.'); | ||
var pattern = new Pattern('(/)users(/:foo)/bar(/*)'); | ||
``` | ||
the default separator is `/`. you can pass a custom separator | ||
as the second argument to `Pattern`. | ||
optional matches are stored in the corresponding property, if they exist: | ||
##### match pattern against domain | ||
```javascript | ||
> pattern.match('users/bar'); | ||
{} | ||
match returns the extracted parameters or `null` if there was no match: | ||
> pattern.match('/users/bar'); | ||
{} | ||
```javascript | ||
pattern.match('www.google.com'); // => {sub: 'www'} | ||
pattern.match('www.google.io'); // => null | ||
``` | ||
> pattern.match('/users/biff/bar'); | ||
{foo: 'biff'} | ||
##### make pattern from regex | ||
```javascript | ||
var regexPattern = new Pattern(/example\.(.*)/); | ||
> pattern.match('/users/biff/bar/beep/boop'); | ||
{foo: 'biff', _: 'beep/boop'} | ||
``` | ||
##### match regex pattern against domain | ||
### matching domains | ||
if the pattern was created from a regex an array of the captured groups is returned on match: | ||
``` javascript | ||
> var pattern = new Pattern(':sub.google.com'); | ||
```javascript | ||
regexPattern.match('example.com'); // => ['com'] | ||
regexPattern.match('google.com'); // => null | ||
``` | ||
> pattern.match('www.google.com'); | ||
{sub: 'www'} | ||
##### make wildcard pattern from string | ||
```javascript | ||
var wildcardPattern = new Pattern('*.:sub.google.*'); | ||
> pattern.match('www.google.io'); | ||
null | ||
``` | ||
##### match wildcard pattern against url | ||
``` javascript | ||
> var pattern = new Pattern('*.:sub.google.*'); | ||
wildcard matches are collected in the `_` property: | ||
```javascript | ||
wildcardPattern.match('subsub.www.google.com'); | ||
// => {sub: 'www', _: ['subsub', 'com']} | ||
> pattern.match('subsub.www.google.com');; | ||
{sub: 'www', _: ['subsub', 'com']} | ||
``` | ||
@@ -155,3 +155,3 @@ | ||
``` js | ||
``` javascript | ||
var urlPattern = require('url-pattern'); | ||
@@ -163,6 +163,41 @@ var pattern = urlPattern.newPattern('/example'); | ||
``` js | ||
``` javascript | ||
var Pattern = require('url-pattern'); | ||
var pattern = new Pattern('/example'); | ||
``` | ||
#### 0.8 | ||
single wildcard matches are now saved directly as a | ||
string on the `_` property and not as an array with 1 element: | ||
``` javascript | ||
> var pattern = new Pattern('/api/*'); | ||
> pattern.match('/api/users/5') | ||
{_: 'users/5'} | ||
``` | ||
if named segments occur more than once the results are collected in an array. | ||
parsing of named segment names (`:foo`) and named segment values now | ||
stops at the next non-alphanumeric character. | ||
it is no longer needed to declare separators other than `/` explicitely. | ||
it was previously necessary to use the second argument to `new UrlPattern` to | ||
override the default separator `/`. | ||
the second argument is now ignored. | ||
mixing of separators is now possible (`/` and `.` in this example): | ||
``` javascript | ||
> var pattern = new UrlPattern('/v:major(.:minor)/*'); | ||
> pattern.match('/v1.2/'); | ||
{major: '1', minor: '2', _: ''} | ||
> pattern.match('/v2/users'); | ||
{major: '2', _: 'users'} | ||
> pattern.match('/v/'); | ||
null | ||
``` | ||
### contribution | ||
@@ -176,24 +211,8 @@ | ||
i will only merge pull requests that modify/add functionality | ||
if the changes align with my goals for this package | ||
and only if the changes are well written, documented and tested. | ||
if the changes align with my goals for this package, | ||
are well written, documented and tested. | ||
**communicate:** write an issue to start a discussion | ||
**communicate!** write an issue to start a discussion | ||
before writing code that may or may not get merged. | ||
### todo | ||
- https://github.com/snd/url-pattern/issues/6 | ||
- parse string into array of objects describing structure | ||
- constant | ||
- binding | ||
- wildcard | ||
- optional | ||
- multiple occurences of the same name are collected into an array | ||
- this elegantly normalizes * and : | ||
- binding parsing is flexible and has a start and end regex | ||
- default: `:` and `[^a-zA-Z0-9] | ||
- custom: `#{` and `}` | ||
- test that empty names are not allowed | ||
- browser tests | ||
## [license: MIT](LICENSE) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
26895
143
214