mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 17:34:47 -05:00
feat: add URLPattern API (#11941)
This adds support for the URLPattern API. The API is added in --unstable only, as it has not yet shipped in any browser. It is targeted for shipping in Chrome 95. Spec: https://wicg.github.io/urlpattern/ Co-authored-by: crowlKats < crowlkats@toaxl.com >
This commit is contained in:
parent
2de5587547
commit
e07f28d301
11 changed files with 589 additions and 3 deletions
55
Cargo.lock
generated
55
Cargo.lock
generated
|
@ -923,6 +923,7 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_repr",
|
"serde_repr",
|
||||||
|
"urlpattern",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -4223,6 +4224,47 @@ version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eeba86d422ce181a719445e51872fa30f1f7413b62becb52e95ec91aa262d85c"
|
checksum = "eeba86d422ce181a719445e51872fa30f1f7413b62becb52e95ec91aa262d85c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unic-char-property"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221"
|
||||||
|
dependencies = [
|
||||||
|
"unic-char-range",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unic-char-range"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unic-common"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unic-ucd-ident"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987"
|
||||||
|
dependencies = [
|
||||||
|
"unic-char-property",
|
||||||
|
"unic-char-range",
|
||||||
|
"unic-ucd-version",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unic-ucd-version"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4"
|
||||||
|
dependencies = [
|
||||||
|
"unic-common",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicase"
|
name = "unicase"
|
||||||
version = "2.6.0"
|
version = "2.6.0"
|
||||||
|
@ -4290,6 +4332,19 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "urlpattern"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fbe1da4e25c8758a07ac5b97fe72dec49416ea0783bfa9d6c24793c3a34f1e4e"
|
||||||
|
dependencies = [
|
||||||
|
"derive_more",
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"unic-ucd-ident",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf-8"
|
name = "utf-8"
|
||||||
version = "0.7.6"
|
version = "0.7.6"
|
||||||
|
|
45
cli/tests/unit/urlpattern_test.ts
Normal file
45
cli/tests/unit/urlpattern_test.ts
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||||
|
import { assert, assertEquals, unitTest } from "./test_util.ts";
|
||||||
|
|
||||||
|
unitTest(function urlPatternFromString() {
|
||||||
|
const pattern = new URLPattern("https://deno.land/foo/:bar");
|
||||||
|
assertEquals(pattern.protocol, "https");
|
||||||
|
assertEquals(pattern.hostname, "deno.land");
|
||||||
|
assertEquals(pattern.pathname, "/foo/:bar");
|
||||||
|
|
||||||
|
assert(pattern.test("https://deno.land/foo/x"));
|
||||||
|
assert(!pattern.test("https://deno.com/foo/x"));
|
||||||
|
const match = pattern.exec("https://deno.land/foo/x");
|
||||||
|
assert(match);
|
||||||
|
assertEquals(match.pathname.input, "/foo/x");
|
||||||
|
assertEquals(match.pathname.groups, { bar: "x" });
|
||||||
|
});
|
||||||
|
|
||||||
|
unitTest(function urlPatternFromStringWithBase() {
|
||||||
|
const pattern = new URLPattern("/foo/:bar", "https://deno.land");
|
||||||
|
assertEquals(pattern.protocol, "https");
|
||||||
|
assertEquals(pattern.hostname, "deno.land");
|
||||||
|
assertEquals(pattern.pathname, "/foo/:bar");
|
||||||
|
|
||||||
|
assert(pattern.test("https://deno.land/foo/x"));
|
||||||
|
assert(!pattern.test("https://deno.com/foo/x"));
|
||||||
|
const match = pattern.exec("https://deno.land/foo/x");
|
||||||
|
assert(match);
|
||||||
|
assertEquals(match.pathname.input, "/foo/x");
|
||||||
|
assertEquals(match.pathname.groups, { bar: "x" });
|
||||||
|
});
|
||||||
|
|
||||||
|
unitTest(function urlPatternFromInit() {
|
||||||
|
const pattern = new URLPattern({
|
||||||
|
pathname: "/foo/:bar",
|
||||||
|
});
|
||||||
|
assertEquals(pattern.protocol, "*");
|
||||||
|
assertEquals(pattern.hostname, "*");
|
||||||
|
assertEquals(pattern.pathname, "/foo/:bar");
|
||||||
|
|
||||||
|
assert(pattern.test("https://deno.land/foo/x"));
|
||||||
|
assert(pattern.test("https://deno.com/foo/x"));
|
||||||
|
assert(!pattern.test("https://deno.com/bar/x"));
|
||||||
|
|
||||||
|
assert(pattern.test({ pathname: "/foo/x" }));
|
||||||
|
});
|
269
ext/url/01_urlpattern.js
Normal file
269
ext/url/01_urlpattern.js
Normal file
|
@ -0,0 +1,269 @@
|
||||||
|
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
// @ts-check
|
||||||
|
/// <reference path="../../core/internal.d.ts" />
|
||||||
|
/// <reference path="../../core/lib.deno_core.d.ts" />
|
||||||
|
/// <reference path="../webidl/internal.d.ts" />
|
||||||
|
/// <reference path="./internal.d.ts" />
|
||||||
|
/// <reference path="./lib.deno_url.d.ts" />
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
((window) => {
|
||||||
|
const core = window.Deno.core;
|
||||||
|
const webidl = window.__bootstrap.webidl;
|
||||||
|
const {
|
||||||
|
ArrayPrototypeMap,
|
||||||
|
ObjectKeys,
|
||||||
|
ObjectFromEntries,
|
||||||
|
RegExp,
|
||||||
|
RegExpPrototypeExec,
|
||||||
|
RegExpPrototypeTest,
|
||||||
|
Symbol,
|
||||||
|
SymbolFor,
|
||||||
|
TypeError,
|
||||||
|
} = window.__bootstrap.primordials;
|
||||||
|
|
||||||
|
const _components = Symbol("components");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef Components
|
||||||
|
* @property {Component} protocol
|
||||||
|
* @property {Component} username
|
||||||
|
* @property {Component} password
|
||||||
|
* @property {Component} hostname
|
||||||
|
* @property {Component} port
|
||||||
|
* @property {Component} pathname
|
||||||
|
* @property {Component} search
|
||||||
|
* @property {Component} hash
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef Component
|
||||||
|
* @property {string} patternString
|
||||||
|
* @property {RegExp} regexp
|
||||||
|
* @property {string[]} groupNameList
|
||||||
|
*/
|
||||||
|
|
||||||
|
class URLPattern {
|
||||||
|
/** @type {Components} */
|
||||||
|
[_components];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {URLPatternInput} input
|
||||||
|
* @param {string} [baseURL]
|
||||||
|
*/
|
||||||
|
constructor(input, baseURL = undefined) {
|
||||||
|
this[webidl.brand] = webidl.brand;
|
||||||
|
const prefix = "Failed to construct 'URLPattern'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
input = webidl.converters.URLPatternInput(input, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
if (baseURL !== undefined) {
|
||||||
|
baseURL = webidl.converters.USVString(baseURL, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 2",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const components = core.opSync("op_urlpattern_parse", input, baseURL);
|
||||||
|
|
||||||
|
for (const key of ObjectKeys(components)) {
|
||||||
|
try {
|
||||||
|
components[key].regexp = new RegExp(
|
||||||
|
components[key].regexpString,
|
||||||
|
"u",
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
throw new TypeError(`${prefix}: ${key} is invalid; ${e.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this[_components] = components;
|
||||||
|
}
|
||||||
|
|
||||||
|
get protocol() {
|
||||||
|
webidl.assertBranded(this, URLPattern);
|
||||||
|
return this[_components].protocol.patternString;
|
||||||
|
}
|
||||||
|
|
||||||
|
get username() {
|
||||||
|
webidl.assertBranded(this, URLPattern);
|
||||||
|
return this[_components].username.patternString;
|
||||||
|
}
|
||||||
|
|
||||||
|
get password() {
|
||||||
|
webidl.assertBranded(this, URLPattern);
|
||||||
|
return this[_components].password.patternString;
|
||||||
|
}
|
||||||
|
|
||||||
|
get hostname() {
|
||||||
|
webidl.assertBranded(this, URLPattern);
|
||||||
|
return this[_components].hostname.patternString;
|
||||||
|
}
|
||||||
|
|
||||||
|
get port() {
|
||||||
|
webidl.assertBranded(this, URLPattern);
|
||||||
|
return this[_components].port.patternString;
|
||||||
|
}
|
||||||
|
|
||||||
|
get pathname() {
|
||||||
|
webidl.assertBranded(this, URLPattern);
|
||||||
|
return this[_components].pathname.patternString;
|
||||||
|
}
|
||||||
|
|
||||||
|
get search() {
|
||||||
|
webidl.assertBranded(this, URLPattern);
|
||||||
|
return this[_components].search.patternString;
|
||||||
|
}
|
||||||
|
|
||||||
|
get hash() {
|
||||||
|
webidl.assertBranded(this, URLPattern);
|
||||||
|
return this[_components].hash.patternString;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {URLPatternInput} input
|
||||||
|
* @param {string} [baseURL]
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
test(input, baseURL = undefined) {
|
||||||
|
webidl.assertBranded(this, URLPattern);
|
||||||
|
const prefix = "Failed to execute 'test' on 'URLPattern'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
input = webidl.converters.URLPatternInput(input, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
if (baseURL !== undefined) {
|
||||||
|
baseURL = webidl.converters.USVString(baseURL, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 2",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = core.opSync(
|
||||||
|
"op_urlpattern_process_match_input",
|
||||||
|
input,
|
||||||
|
baseURL,
|
||||||
|
);
|
||||||
|
if (res === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [values] = res;
|
||||||
|
|
||||||
|
for (const key of ObjectKeys(values)) {
|
||||||
|
if (!RegExpPrototypeTest(this[_components][key].regexp, values[key])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {URLPatternInput} input
|
||||||
|
* @param {string} [baseURL]
|
||||||
|
* @returns {URLPatternResult | null}
|
||||||
|
*/
|
||||||
|
exec(input, baseURL = undefined) {
|
||||||
|
webidl.assertBranded(this, URLPattern);
|
||||||
|
const prefix = "Failed to execute 'exec' on 'URLPattern'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
input = webidl.converters.URLPatternInput(input, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
if (baseURL !== undefined) {
|
||||||
|
baseURL = webidl.converters.USVString(baseURL, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 2",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = core.opSync(
|
||||||
|
"op_urlpattern_process_match_input",
|
||||||
|
input,
|
||||||
|
baseURL,
|
||||||
|
);
|
||||||
|
if (res === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [values, inputs] = res;
|
||||||
|
if (inputs[1] === null) {
|
||||||
|
inputs.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {URLPatternResult} */
|
||||||
|
const result = { inputs };
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
for (const key of ObjectKeys(values)) {
|
||||||
|
/** @type {Component} */
|
||||||
|
const component = this[_components][key];
|
||||||
|
const input = values[key];
|
||||||
|
const match = RegExpPrototypeExec(component.regexp, input);
|
||||||
|
if (match === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const groupEntries = ArrayPrototypeMap(
|
||||||
|
component.groupNameList,
|
||||||
|
(name, i) => [name, match[i + 1] ?? ""],
|
||||||
|
);
|
||||||
|
const groups = ObjectFromEntries(groupEntries);
|
||||||
|
result[key] = {
|
||||||
|
input,
|
||||||
|
groups,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[SymbolFor("Deno.customInspect")](inspect) {
|
||||||
|
return `URLPattern ${
|
||||||
|
inspect({
|
||||||
|
protocol: this.protocol,
|
||||||
|
username: this.username,
|
||||||
|
password: this.password,
|
||||||
|
hostname: this.hostname,
|
||||||
|
port: this.port,
|
||||||
|
pathname: this.pathname,
|
||||||
|
search: this.search,
|
||||||
|
hash: this.hash,
|
||||||
|
})
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
webidl.configurePrototype(URLPattern);
|
||||||
|
|
||||||
|
webidl.converters.URLPatternInit = webidl
|
||||||
|
.createDictionaryConverter("URLPatternInit", [
|
||||||
|
{ key: "protocol", converter: webidl.converters.USVString },
|
||||||
|
{ key: "username", converter: webidl.converters.USVString },
|
||||||
|
{ key: "password", converter: webidl.converters.USVString },
|
||||||
|
{ key: "hostname", converter: webidl.converters.USVString },
|
||||||
|
{ key: "port", converter: webidl.converters.USVString },
|
||||||
|
{ key: "pathname", converter: webidl.converters.USVString },
|
||||||
|
{ key: "search", converter: webidl.converters.USVString },
|
||||||
|
{ key: "hash", converter: webidl.converters.USVString },
|
||||||
|
{ key: "baseURL", converter: webidl.converters.USVString },
|
||||||
|
]);
|
||||||
|
|
||||||
|
webidl.converters["URLPatternInput"] = (V, opts) => {
|
||||||
|
// Union for (URLPatternInit or USVString)
|
||||||
|
if (typeof V == "object") {
|
||||||
|
return webidl.converters.URLPatternInit(V, opts);
|
||||||
|
}
|
||||||
|
return webidl.converters.USVString(V, opts);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.__bootstrap.urlPattern = {
|
||||||
|
URLPattern,
|
||||||
|
};
|
||||||
|
})(globalThis);
|
|
@ -19,6 +19,7 @@ idna = "0.2.3"
|
||||||
percent-encoding = "2.1.0"
|
percent-encoding = "2.1.0"
|
||||||
serde = { version = "1.0.129", features = ["derive"] }
|
serde = { version = "1.0.129", features = ["derive"] }
|
||||||
serde_repr = "0.1.7"
|
serde_repr = "0.1.7"
|
||||||
|
urlpattern = "0.1.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
deno_bench_util = { version = "0.10.0", path = "../../bench_util" }
|
deno_bench_util = { version = "0.10.0", path = "../../bench_util" }
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
# deno_url
|
# deno_url
|
||||||
|
|
||||||
This crate implements the URL API for Deno.
|
This crate implements the URL, and URLPattern APIs for Deno.
|
||||||
|
|
||||||
Spec: https://url.spec.whatwg.org/
|
URL Spec: https://url.spec.whatwg.org/ URLPattern Spec:
|
||||||
|
https://wicg.github.io/urlpattern/
|
||||||
|
|
4
ext/url/internal.d.ts
vendored
4
ext/url/internal.d.ts
vendored
|
@ -10,5 +10,9 @@ declare namespace globalThis {
|
||||||
URLSearchParams: typeof URLSearchParams;
|
URLSearchParams: typeof URLSearchParams;
|
||||||
parseUrlEncoded(bytes: Uint8Array): [string, string][];
|
parseUrlEncoded(bytes: Uint8Array): [string, string][];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
declare var urlPattern: {
|
||||||
|
URLPattern: typeof URLPattern;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
136
ext/url/lib.deno_url.d.ts
vendored
136
ext/url/lib.deno_url.d.ts
vendored
|
@ -172,3 +172,139 @@ declare class URL {
|
||||||
username: string;
|
username: string;
|
||||||
toJSON(): string;
|
toJSON(): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare interface URLPatternInit {
|
||||||
|
protocol?: string;
|
||||||
|
username?: string;
|
||||||
|
password?: string;
|
||||||
|
hostname?: string;
|
||||||
|
port?: string;
|
||||||
|
pathname?: string;
|
||||||
|
search?: string;
|
||||||
|
hash?: string;
|
||||||
|
baseURL?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type URLPatternInput = string | URLPatternInit;
|
||||||
|
|
||||||
|
declare interface URLPatternComponentResult {
|
||||||
|
input: string;
|
||||||
|
groups: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** `URLPatternResult` is the object returned from `URLPattern.match`. */
|
||||||
|
declare interface URLPatternResult {
|
||||||
|
/** The inputs provided when matching. */
|
||||||
|
inputs: [URLPatternInit] | [URLPatternInit, string];
|
||||||
|
|
||||||
|
/** The matched result for the `protocol` matcher. */
|
||||||
|
protocol: URLPatternComponentResult;
|
||||||
|
/** The matched result for the `username` matcher. */
|
||||||
|
username: URLPatternComponentResult;
|
||||||
|
/** The matched result for the `password` matcher. */
|
||||||
|
password: URLPatternComponentResult;
|
||||||
|
/** The matched result for the `hostname` matcher. */
|
||||||
|
hostname: URLPatternComponentResult;
|
||||||
|
/** The matched result for the `port` matcher. */
|
||||||
|
port: URLPatternComponentResult;
|
||||||
|
/** The matched result for the `pathname` matcher. */
|
||||||
|
pathname: URLPatternComponentResult;
|
||||||
|
/** The matched result for the `search` matcher. */
|
||||||
|
search: URLPatternComponentResult;
|
||||||
|
/** The matched result for the `hash` matcher. */
|
||||||
|
hash: URLPatternComponentResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The URLPattern API provides a web platform primitive for matching URLs based
|
||||||
|
* on a convenient pattern syntax.
|
||||||
|
*
|
||||||
|
* The syntax is based on path-to-regexp. Wildcards, named capture groups,
|
||||||
|
* regular groups, and group modifiers are all supported.
|
||||||
|
*
|
||||||
|
* ```ts
|
||||||
|
* // Specify the pattern as structured data.
|
||||||
|
* const pattern = new URLPattern({ pathname: "/users/:user" });
|
||||||
|
* const match = pattern.match("/users/joe");
|
||||||
|
* console.log(match.pathname.groups.user); // joe
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* ```ts
|
||||||
|
* // Specify a fully qualified string pattern.
|
||||||
|
* const pattern = new URLPattern("https://example.com/books/:id");
|
||||||
|
* console.log(pattern.test("https://example.com/books/123")); // true
|
||||||
|
* console.log(pattern.test("https://deno.land/books/123")); // false
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* ```ts
|
||||||
|
* // Specify a relative string pattern with a base URL.
|
||||||
|
* const pattern = new URLPattern("/:article", "https://blog.example.com");
|
||||||
|
* console.log(pattern.test("https://blog.example.com/article")); // true
|
||||||
|
* console.log(pattern.test("https://blog.example.com/article/123")); // false
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
declare class URLPattern {
|
||||||
|
constructor(input: URLPatternInput, baseURL?: string);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test if the given input matches the stored pattern.
|
||||||
|
*
|
||||||
|
* The input can either be provided as a url string (with an optional base),
|
||||||
|
* or as individual components in the form of an object.
|
||||||
|
*
|
||||||
|
* ```ts
|
||||||
|
* const pattern = new URLPattern("https://example.com/books/:id");
|
||||||
|
*
|
||||||
|
* // Test a url string.
|
||||||
|
* console.log(pattern.test("https://example.com/books/123")); // true
|
||||||
|
*
|
||||||
|
* // Test a relative url with a base.
|
||||||
|
* console.log(pattern.test("/books/123", "https://example.com")); // true
|
||||||
|
*
|
||||||
|
* // Test an object of url components.
|
||||||
|
* console.log(pattern.test({ pathname: "/books/123" })); // true
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
test(input: URLPatternInput, baseURL?: string): boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Match the given input against the stored pattern.
|
||||||
|
*
|
||||||
|
* The input can either be provided as a url string (with an optional base),
|
||||||
|
* or as individual components in the form of an object.
|
||||||
|
*
|
||||||
|
* ```ts
|
||||||
|
* const pattern = new URLPattern("https://example.com/books/:id");
|
||||||
|
*
|
||||||
|
* // Match a url string.
|
||||||
|
* let match = pattern.match("https://example.com/books/123");
|
||||||
|
* console.log(match.pathname.groups.id); // 123
|
||||||
|
*
|
||||||
|
* // Match a relative url with a base.
|
||||||
|
* match = pattern.match("/books/123", "https://example.com");
|
||||||
|
* console.log(match.pathname.groups.id); // 123
|
||||||
|
*
|
||||||
|
* // Match an object of url components.
|
||||||
|
* match = pattern.match({ pathname: "/books/123" });
|
||||||
|
* console.log(match.pathname.groups.id); // 123
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
exec(input: URLPatternInput, baseURL?: string): URLPatternResult | null;
|
||||||
|
|
||||||
|
/** The pattern string for the `protocol`. */
|
||||||
|
readonly protocol: string;
|
||||||
|
/** The pattern string for the `username`. */
|
||||||
|
readonly username: string;
|
||||||
|
/** The pattern string for the `password`. */
|
||||||
|
readonly password: string;
|
||||||
|
/** The pattern string for the `hostname`. */
|
||||||
|
readonly hostname: string;
|
||||||
|
/** The pattern string for the `port`. */
|
||||||
|
readonly port: string;
|
||||||
|
/** The pattern string for the `pathname`. */
|
||||||
|
readonly pathname: string;
|
||||||
|
/** The pattern string for the `search`. */
|
||||||
|
readonly search: string;
|
||||||
|
/** The pattern string for the `hash`. */
|
||||||
|
readonly hash: string;
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
mod urlpattern;
|
||||||
|
|
||||||
use deno_core::error::generic_error;
|
use deno_core::error::generic_error;
|
||||||
use deno_core::error::type_error;
|
use deno_core::error::type_error;
|
||||||
use deno_core::error::uri_error;
|
use deno_core::error::uri_error;
|
||||||
|
@ -14,11 +16,15 @@ use deno_core::ZeroCopyBuf;
|
||||||
use std::panic::catch_unwind;
|
use std::panic::catch_unwind;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use crate::urlpattern::op_urlpattern_parse;
|
||||||
|
use crate::urlpattern::op_urlpattern_process_match_input;
|
||||||
|
|
||||||
pub fn init() -> Extension {
|
pub fn init() -> Extension {
|
||||||
Extension::builder()
|
Extension::builder()
|
||||||
.js(include_js_files!(
|
.js(include_js_files!(
|
||||||
prefix "deno:ext/url",
|
prefix "deno:ext/url",
|
||||||
"00_url.js",
|
"00_url.js",
|
||||||
|
"01_urlpattern.js",
|
||||||
))
|
))
|
||||||
.ops(vec![
|
.ops(vec![
|
||||||
("op_url_parse", op_sync(op_url_parse)),
|
("op_url_parse", op_sync(op_url_parse)),
|
||||||
|
@ -31,6 +37,11 @@ pub fn init() -> Extension {
|
||||||
"op_url_stringify_search_params",
|
"op_url_stringify_search_params",
|
||||||
op_sync(op_url_stringify_search_params),
|
op_sync(op_url_stringify_search_params),
|
||||||
),
|
),
|
||||||
|
("op_urlpattern_parse", op_sync(op_urlpattern_parse)),
|
||||||
|
(
|
||||||
|
"op_urlpattern_process_match_input",
|
||||||
|
op_sync(op_urlpattern_process_match_input),
|
||||||
|
),
|
||||||
])
|
])
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
40
ext/url/urlpattern.rs
Normal file
40
ext/url/urlpattern.rs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
use deno_core::error::type_error;
|
||||||
|
use deno_core::error::AnyError;
|
||||||
|
|
||||||
|
use urlpattern::quirks;
|
||||||
|
use urlpattern::quirks::MatchInput;
|
||||||
|
use urlpattern::quirks::StringOrInit;
|
||||||
|
use urlpattern::quirks::UrlPattern;
|
||||||
|
|
||||||
|
pub fn op_urlpattern_parse(
|
||||||
|
_state: &mut deno_core::OpState,
|
||||||
|
input: StringOrInit,
|
||||||
|
base_url: Option<String>,
|
||||||
|
) -> Result<UrlPattern, AnyError> {
|
||||||
|
let init = urlpattern::quirks::process_construct_pattern_input(
|
||||||
|
input,
|
||||||
|
base_url.as_deref(),
|
||||||
|
)
|
||||||
|
.map_err(|e| type_error(e.to_string()))?;
|
||||||
|
|
||||||
|
let pattern = urlpattern::quirks::parse_pattern(init)
|
||||||
|
.map_err(|e| type_error(e.to_string()))?;
|
||||||
|
|
||||||
|
Ok(pattern)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn op_urlpattern_process_match_input(
|
||||||
|
_state: &mut deno_core::OpState,
|
||||||
|
input: StringOrInit,
|
||||||
|
base_url: Option<String>,
|
||||||
|
) -> Result<Option<(MatchInput, quirks::Inputs)>, AnyError> {
|
||||||
|
let res = urlpattern::quirks::process_match_input(input, base_url.as_deref())
|
||||||
|
.map_err(|e| type_error(e.to_string()))?;
|
||||||
|
|
||||||
|
let (input, inputs) = match res {
|
||||||
|
Some((input, inputs)) => (input, inputs),
|
||||||
|
None => return Ok(None),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(urlpattern::quirks::parse_match_input(input).map(|input| (input, inputs)))
|
||||||
|
}
|
|
@ -41,6 +41,7 @@ delete Object.prototype.__proto__;
|
||||||
const performance = window.__bootstrap.performance;
|
const performance = window.__bootstrap.performance;
|
||||||
const crypto = window.__bootstrap.crypto;
|
const crypto = window.__bootstrap.crypto;
|
||||||
const url = window.__bootstrap.url;
|
const url = window.__bootstrap.url;
|
||||||
|
const urlPattern = window.__bootstrap.urlPattern;
|
||||||
const headers = window.__bootstrap.headers;
|
const headers = window.__bootstrap.headers;
|
||||||
const streams = window.__bootstrap.streams;
|
const streams = window.__bootstrap.streams;
|
||||||
const fileReader = window.__bootstrap.fileReader;
|
const fileReader = window.__bootstrap.fileReader;
|
||||||
|
@ -431,8 +432,9 @@ delete Object.prototype.__proto__;
|
||||||
};
|
};
|
||||||
|
|
||||||
const unstableWindowOrWorkerGlobalScope = {
|
const unstableWindowOrWorkerGlobalScope = {
|
||||||
WebSocketStream: util.nonEnumerable(webSocket.WebSocketStream),
|
|
||||||
BroadcastChannel: util.nonEnumerable(broadcastChannel.BroadcastChannel),
|
BroadcastChannel: util.nonEnumerable(broadcastChannel.BroadcastChannel),
|
||||||
|
URLPattern: util.nonEnumerable(urlPattern.URLPattern),
|
||||||
|
WebSocketStream: util.nonEnumerable(webSocket.WebSocketStream),
|
||||||
|
|
||||||
GPU: util.nonEnumerable(webgpu.GPU),
|
GPU: util.nonEnumerable(webgpu.GPU),
|
||||||
GPUAdapter: util.nonEnumerable(webgpu.GPUAdapter),
|
GPUAdapter: util.nonEnumerable(webgpu.GPUAdapter),
|
||||||
|
|
|
@ -13490,6 +13490,14 @@
|
||||||
],
|
],
|
||||||
"toString.tentative.any.html": false,
|
"toString.tentative.any.html": false,
|
||||||
"type.tentative.any.html": false
|
"type.tentative.any.html": false
|
||||||
|
},
|
||||||
|
"function": {
|
||||||
|
"call.tentative.any.html": false,
|
||||||
|
"constructor.tentative.any.html": [
|
||||||
|
"construct with JS function"
|
||||||
|
],
|
||||||
|
"table.tentative.any.html": false,
|
||||||
|
"type.tentative.any.html": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"serialization": {
|
"serialization": {
|
||||||
|
@ -14527,5 +14535,19 @@
|
||||||
"performance.clearResourceTimings in workers",
|
"performance.clearResourceTimings in workers",
|
||||||
"performance.setResourceTimingBufferSize in workers"
|
"performance.setResourceTimingBufferSize in workers"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"urlpattern": {
|
||||||
|
"urlpattern.any.html": [
|
||||||
|
"Pattern: [{\"pathname\":\"/foo/bar\"}] Inputs: [\"./foo/bar\",\"https://example.com\"]"
|
||||||
|
],
|
||||||
|
"urlpattern.any.worker.html": [
|
||||||
|
"Pattern: [{\"pathname\":\"/foo/bar\"}] Inputs: [\"./foo/bar\",\"https://example.com\"]"
|
||||||
|
],
|
||||||
|
"urlpattern.https.any.html": [
|
||||||
|
"Pattern: [{\"pathname\":\"/foo/bar\"}] Inputs: [\"./foo/bar\",\"https://example.com\"]"
|
||||||
|
],
|
||||||
|
"urlpattern.https.any.worker.html": [
|
||||||
|
"Pattern: [{\"pathname\":\"/foo/bar\"}] Inputs: [\"./foo/bar\",\"https://example.com\"]"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue