easier than regex string matching patterns for urls and other strings.
turn strings into data or data into strings.
This is a great little library -- thanks!
michael
> const pattern = new UrlPattern("/api/users(/:id)");match a pattern against a string and extract values:
> pattern.match("/api/users/10");
{id: "10"}
> pattern.match("/api/users");
{}
> pattern.match("/api/products/5");
undefinedgenerate a string from a pattern and values:
> pattern.stringify()
"/api/users"
> pattern.stringify({id: 20})
"/api/users/20"prefer a different syntax? customize it:
> const pattern = new UrlPattern("/api/users/{id}", {
segmentNameEndChar: "}",
segmentNameStartChar: "{",
}
> pattern.match("/api/users/5")
{id: "5"}- very fast matching as each pattern is compiled into a regex
- tiny source of around 500 lines of simple, readable typescript
- widely used
- zero dependencies
- parser implemented using simple, precise, reusable parser combinators
- continuously tested in Node.js (10.15 (LTS), 12) and all relevant browsers
- huge test suite
with
code coverage
- customizable pattern syntax
- frequently asked questions
> const pattern = new UrlPattern("(http(s)\\://)(:subdomain.):domain.:tld(\\::port)(/*:path)")
> pattern.match("google.de");
{domain: "google", tld: "de"}
> pattern.match("https://www.google.com");
{subdomain: "www", domain: "google", tld: "com"}
> pattern.match("http://mail.google.com/mail");
{subdomain: "mail", domain: "google", tld: "com", path: "mail"}
> pattern.match("http://mail.google.com:80/mail/inbox");
{subdomain: "mail", domain: "google", tld: "com", port: "80", path: "mail/inbox"}
> pattern.match("google");
undefinednpm install url-pattern
and
> import UrlPattern from "url-pattern";or
> const UrlPattern = require("url-pattern").default;works with deno:
stable latest release:
import UrlPattern from "https://raw.githubusercontent.com/snd/url-pattern/2.0.0/src/url-pattern.ts";bleeding edge master:
import UrlPattern from "https://raw.githubusercontent.com/snd/url-pattern/master/src/url-pattern.ts";> const pattern = new UrlPattern("/api/users/:id");a UrlPattern is immutable after construction.
none of its methods changes its state.
that makes it easier to reason about.
match returns the extracted segments:
> pattern.match("/api/users/10");
{id: "10"}or undefined if there was no match:
> pattern.match("/api/products/5");
undefinedpatterns are compiled into regexes which makes .match() superfast.
:id (in the example above) is a named segment:
a named segment starts with : followed by the name.
the name must be at least one character in the regex character set a-zA-Z0-9_.
when matching, a named segment consumes all characters in the regex character set
a-zA-Z0-9-_~ %.
a named segment match stops at /, ., ... but not at _, -, , %...
you can change these character sets. click here to see how.
names must be unique. a name may not appear twice in a pattern.
to make part of a pattern optional just wrap it in ( and ):
> const pattern = new UrlPattern(
"(http(s)\\://)(:subdomain.):domain.:tld(/*:path)"
);note that \\ escapes the : in http(s)\\://.
you can use \\ to escape (, ), : and * which have special meaning within
url-pattern.
optional named segments are stored in the corresponding property only if they are present in the source string:
> pattern.match("google.de");
{domain: "google", tld: "de"}> pattern.match("https://www.google.com");
{subdomain: "www", domain: "google", tld: "com"}:*path in the pattern above is a named wildcard with the name path.
named wildcards match anything. that makes them different from named segments which
only match characters inside the options.segmentNameCharset (default: a-zA-Z0-9_-).
> pattern.match("http://mail.google.com/mail/inbox");
{subdomain: "mail", domain: "google", tld: "com", path: "mail/inbox"}there are also
unnamed wildcards are not collected.
> const pattern = new UrlPattern('/search/*:term');
> pattern.match('/search/fruit');
{term: 'fruit'}look at the tests for additional examples of .match
> const pattern = new UrlPattern(/^\/api\/(.*)$/, ["path"]);
> pattern.match("/api/users");
{path: "users"}
> pattern.match("/apiii/test");
undefinedwhen making a pattern from a regex you have to pass an array of keys as the second argument. returns objects on match with each key mapped to a captured value:
> const pattern = new UrlPattern(
/^\/api\/([^\/]+)(?:\/(\d+))?$/,
["resource", "id"]
);
> pattern.match("/api/users");
{resource: "users"}
> pattern.match("/api/users/5");
{resource: "users", id: "5"}
> pattern.match("/api/users/foo");
undefined> const pattern = new UrlPattern("/api/users/:id");
> pattern.stringify({id: 10})
"/api/users/10"optional segments are only included in the output if they contain named segments and/or wildcards and values for those are provided:
> const pattern = new UrlPattern("/api/users(/:id)");
> pattern.stringify()
"/api/users"
> pattern.stringify({id: 10})
"/api/users/10"named wildcards and deeply nested optional groups should stringify as expected.
TODO an error is thrown if a value that is not in an optional group is not provided.
an error is thrown if an optional segment contains multiple params and not all of them are provided. one provided value for an optional segment makes all values in that optional segment required.
TODO anonymous wildcards are ignored.
look at the tests for additional examples of .stringify
finally we can completely change pattern-parsing and regex-compilation to suit our needs:
> let options = {};let's change the char used for escaping (default \\):
> options.escapeChar = "!";let's change the char used to start a named segment (default :):
> options.segmentNameStartChar = "{";let's add a char required at the end of a named segment (default nothing):
> options.segmentNameEndChar = "}";let's change the set of chars allowed in named segment names (default a-zA-Z0-9_)
to also include -:
> options.segmentNameCharset = "a-zA-Z0-9_-";let's change the set of chars allowed in named segment values
(default a-zA-Z0-9-_~ %) to not allow non-alphanumeric chars:
> options.segmentValueCharset = "a-zA-Z0-9";let's change the chars used to surround an optional segment (default ( and )):
> options.optionalSegmentStartChar = "<";
> options.optionalSegmentEndChar = ">";let's change the char used to denote a wildcard (default *):
> options.wildcardChar = "#";pass options as the second argument to the constructor:
> const pattern = new UrlPattern(
"<http<s>!://><{sub_domain}.>{domain}.{toplevel-domain}</#{path}>",
options
);then match:
> pattern.match("http://mail.google.com/mail");
{
sub_domain: "mail",
domain: "google",
"toplevel-domain": "com",
path: "mail"
}the query part of an URL has very different semantics than the rest. url-pattern is not well suited for parsing the query part.
there are good existing libraries for parsing the query part of an URL. https://github.com/hapijs/qs is an example. in the interest of keeping things simple and focused i see no reason to add special support for parsing the query part to url-pattern.
i recommend splitting the URL at ?, using url-pattern
to parse the first part (scheme, host, port, path)
and using https://github.com/hapijs/qs to parse the last part (query).
you can't exactly match IPs with url-pattern so you have to fall back to regexes and pass in a regex object.