summaryrefslogtreecommitdiff
path: root/ext/url/01_urlpattern.js
diff options
context:
space:
mode:
Diffstat (limited to 'ext/url/01_urlpattern.js')
-rw-r--r--ext/url/01_urlpattern.js269
1 files changed, 269 insertions, 0 deletions
diff --git a/ext/url/01_urlpattern.js b/ext/url/01_urlpattern.js
new file mode 100644
index 000000000..b6ff9e40e
--- /dev/null
+++ b/ext/url/01_urlpattern.js
@@ -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);