Skip to content

Commit

Permalink
url: add URLPattern implementation
Browse files Browse the repository at this point in the history
Co-authored-by: Daniel Lemire <[email protected]>
  • Loading branch information
anonrig and lemire committed Jan 30, 2025
1 parent a4c2d56 commit 9659662
Show file tree
Hide file tree
Showing 14 changed files with 1,074 additions and 0 deletions.
7 changes: 7 additions & 0 deletions doc/api/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -2118,6 +2118,13 @@ constructor][`new URL(input)`] or the legacy [`url.parse()`][] to be parsed.
The thrown error object typically has an additional property `'input'` that
contains the URL that failed to parse.

<a id="ERR_INVALID_URL_PATTERN"></a>

### `ERR_INVALID_URL_PATTERN`

An invalid URLPattern was passed to the [WHATWG][WHATWG URL API] \[`URLPattern`
constructor]\[`new URLPattern(input)`] to be parsed.

<a id="ERR_INVALID_URL_SCHEME"></a>

### `ERR_INVALID_URL_SCHEME`
Expand Down
123 changes: 123 additions & 0 deletions doc/api/url.md
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,129 @@ Parses a string as a URL. If `base` is provided, it will be used as the base
URL for the purpose of resolving non-absolute `input` URLs. Returns `null`
if `input` is not a valid.

### Class: `URLPattern`

> Stability: 1 - Experimental
<!-- YAML
added: REPLACEME
-->

The `URLPattern` API provides an interface to match URLs or parts of URLs
against a pattern.

```js
const myPattern = new URLPattern('https://nodejs.org/docs/latest/api/*.html');
console.log(myPattern.exec('https://nodejs.org/docs/latest/api/dns.html'));
// Prints:
// {
// "hash": { "groups": { "0": "" }, "input": "" },
// "hostname": { "groups": {}, "input": "nodejs.org" },
// "inputs": [
// "https://nodejs.org/docs/latest/api/dns.html"
// ],
// "password": { "groups": { "0": "" }, "input": "" },
// "pathname": { "groups": { "0": "dns" }, "input": "/docs/latest/api/dns.html" },
// "port": { "groups": {}, "input": "" },
// "protocol": { "groups": {}, "input": "https" },
// "search": { "groups": { "0": "" }, "input": "" },
// "username": { "groups": { "0": "" }, "input": "" }
// }

console.log(myPattern.test('https://nodejs.org/docs/latest/api/dns.html'));
// Prints: true
```

#### `new URLPattern()`

Instantiate a new empty `URLPattern` object.

#### `new URLPattern(string[, baseURL][, options])`

* `string` {string} A URL string
* `baseURL` {string | undefined} A base URL string
* `options` {Object} Options

Parse the `string` as a URL, and use it to instantiate a new
`URLPattern` object.

If `baseURL` is not specified, it defaults to `undefined`.

An option can have `ignoreCase` boolean attribute which enables
case-insensitive matching if set to true.

The constructor can throw a `TypeError` to indicate parsing failure.

#### `new URLPattern(objg[, baseURL][, options])`

* `obj` {Object} An input pattern
* `baseURL` {string | undefined} A base URL string
* `options` {Object} Options

Parse the `Object` as an input pattern, and use it to instantiate a new
`URLPattern` object. The object members can be any of `protocol`, `username`,
`password`, `hostname`, `port`, `pathname`, `search`, `hash` or `baseURL`.

If `baseURL` is not specified, it defaults to `undefined`.

An option can have `ignoreCase` boolean attribute which enables
case-insensitive matching if set to true.

The constructor can throw a `TypeError` to indicate parsing failure.

#### `urlPattern.exec(input[, baseURL])`

* `input` {string | Object} A URL or URL parts
* `baseURL` {string | undefined} A base URL string

Input can be a string or an object providing the individual URL parts. The
object members can be any of `protocol`, `username`, `password`, `hostname`,
`port`, `pathname`, `search`, `hash` or `baseURL`.

If `baseURL` is not specified, it will default to `undefined`.

Returns an object with an `inputs` key containing the array of arguments
passed into the function and keys of the URL components which contains the
matched input and matched groups.

```js
const myPattern = new URLPattern('https://nodejs.org/docs/latest/api/*.html');
console.log(myPattern.exec('https://nodejs.org/docs/latest/api/dns.html'));
// Prints:
// {
// "hash": { "groups": { "0": "" }, "input": "" },
// "hostname": { "groups": {}, "input": "nodejs.org" },
// "inputs": [
// "https://nodejs.org/docs/latest/api/dns.html"
// ],
// "password": { "groups": { "0": "" }, "input": "" },
// "pathname": { "groups": { "0": "dns" }, "input": "/docs/latest/api/dns.html" },
// "port": { "groups": {}, "input": "" },
// "protocol": { "groups": {}, "input": "https" },
// "search": { "groups": { "0": "" }, "input": "" },
// "username": { "groups": { "0": "" }, "input": "" }
// }
```

#### `urlPattern.test(input[, baseURL])`

* `input` {string | Object} A URL or URL parts
* `baseURL` {string | undefined} A base URL string

Input can be a string or an object providing the individual URL parts. The
object members can be any of `protocol`, `username`, `password`, `hostname`,
`port`, `pathname`, `search`, `hash` or `baseURL`.

If `baseURL` is not specified, it will default to `undefined`.

Returns a boolean indicating if the input matches the current pattern.

```js
const myPattern = new URLPattern('https://nodejs.org/docs/latest/api/*.html');
console.log(myPattern.test('https://nodejs.org/docs/latest/api/dns.html'));
// Prints: true
```

### Class: `URLSearchParams`

<!-- YAML
Expand Down
2 changes: 2 additions & 0 deletions lib/internal/url.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const {
decodeURIComponent,
} = primordials;

const { URLPattern } = internalBinding('url_pattern');
const { inspect } = require('internal/util/inspect');
const {
encodeStr,
Expand Down Expand Up @@ -1574,6 +1575,7 @@ module.exports = {
toPathIfFileURL,
installObjectURLMethods,
URL,
URLPattern,
URLSearchParams,
URLParse: URL.parse,
domainToASCII,
Expand Down
2 changes: 2 additions & 0 deletions lib/url.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const {
decodeURIComponent,
} = primordials;

const { URLPattern } = internalBinding('url_pattern');
const { toASCII } = internalBinding('encoding_binding');
const { encodeStr, hexTable } = require('internal/querystring');
const querystring = require('querystring');
Expand Down Expand Up @@ -1029,6 +1030,7 @@ module.exports = {

// WHATWG API
URL,
URLPattern,
URLSearchParams,
domainToASCII,
domainToUnicode,
Expand Down
2 changes: 2 additions & 0 deletions node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@
'src/node_trace_events.cc',
'src/node_types.cc',
'src/node_url.cc',
'src/node_url_pattern.cc',
'src/node_util.cc',
'src/node_v8.cc',
'src/node_wasi.cc',
Expand Down Expand Up @@ -275,6 +276,7 @@
'src/node_stat_watcher.h',
'src/node_union_bytes.h',
'src/node_url.h',
'src/node_url_pattern.h',
'src/node_version.h',
'src/node_v8.h',
'src/node_v8_platform-inl.h',
Expand Down
10 changes: 10 additions & 0 deletions src/env_properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
V(async_ids_stack_string, "async_ids_stack") \
V(attributes_string, "attributes") \
V(base_string, "base") \
V(base_url_string, "baseURL") \
V(bits_string, "bits") \
V(block_list_string, "blockList") \
V(buffer_string, "buffer") \
Expand Down Expand Up @@ -179,20 +180,26 @@
V(get_data_clone_error_string, "_getDataCloneError") \
V(get_shared_array_buffer_id_string, "_getSharedArrayBufferId") \
V(gid_string, "gid") \
V(groups_string, "groups") \
V(has_regexp_groups_string, "hasRegExpGroups") \
V(hash_string, "hash") \
V(h2_string, "h2") \
V(handle_string, "handle") \
V(hash_algorithm_string, "hashAlgorithm") \
V(help_text_string, "helpText") \
V(homedir_string, "homedir") \
V(host_string, "host") \
V(hostmaster_string, "hostmaster") \
V(hostname_string, "hostname") \
V(http_1_1_string, "http/1.1") \
V(id_string, "id") \
V(identity_string, "identity") \
V(ignore_case_string, "ignoreCase") \
V(ignore_string, "ignore") \
V(infoaccess_string, "infoAccess") \
V(inherit_string, "inherit") \
V(input_string, "input") \
V(inputs_string, "inputs") \
V(internal_binding_string, "internalBinding") \
V(internal_string, "internal") \
V(ipv4_string, "IPv4") \
Expand Down Expand Up @@ -280,6 +287,7 @@
V(parse_error_string, "Parse Error") \
V(password_string, "password") \
V(path_string, "path") \
V(pathname_string, "pathname") \
V(pending_handle_string, "pendingHandle") \
V(permission_string, "permission") \
V(pid_string, "pid") \
Expand All @@ -295,6 +303,7 @@
V(priority_string, "priority") \
V(process_string, "process") \
V(promise_string, "promise") \
V(protocol_string, "protocol") \
V(prototype_string, "prototype") \
V(psk_string, "psk") \
V(pubkey_string, "pubkey") \
Expand Down Expand Up @@ -323,6 +332,7 @@
V(scopeid_string, "scopeid") \
V(script_id_string, "scriptId") \
V(script_name_string, "scriptName") \
V(search_string, "search") \
V(serial_number_string, "serialNumber") \
V(serial_string, "serial") \
V(servername_string, "servername") \
Expand Down
2 changes: 2 additions & 0 deletions src/node_binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "node_builtins.h"
#include "node_errors.h"
#include "node_external_reference.h"
#include "node_url_pattern.h"
#include "util.h"

#include <string>
Expand Down Expand Up @@ -87,6 +88,7 @@
V(types) \
V(udp_wrap) \
V(url) \
V(url_pattern) \
V(util) \
V(uv) \
V(v8) \
Expand Down
2 changes: 2 additions & 0 deletions src/node_errors.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ void OOMErrorHandler(const char* location, const v8::OOMDetails& details);
V(ERR_INVALID_STATE, Error) \
V(ERR_INVALID_THIS, TypeError) \
V(ERR_INVALID_URL, TypeError) \
V(ERR_INVALID_URL_PATTERN, TypeError) \
V(ERR_INVALID_URL_SCHEME, TypeError) \
V(ERR_LOAD_SQLITE_EXTENSION, Error) \
V(ERR_MEMORY_ALLOCATION_FAILED, Error) \
Expand All @@ -99,6 +100,7 @@ void OOMErrorHandler(const char* location, const v8::OOMDetails& details);
V(ERR_MISSING_PLATFORM_FOR_WORKER, Error) \
V(ERR_MODULE_NOT_FOUND, Error) \
V(ERR_NON_CONTEXT_AWARE_DISABLED, Error) \
V(ERR_OPERATION_FAILED, TypeError) \
V(ERR_OUT_OF_RANGE, RangeError) \
V(ERR_REQUIRE_ASYNC_MODULE, Error) \
V(ERR_SCRIPT_EXECUTION_INTERRUPTED, Error) \
Expand Down
1 change: 1 addition & 0 deletions src/node_external_reference.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ class ExternalReferenceRegistry {
V(tty_wrap) \
V(udp_wrap) \
V(url) \
V(url_pattern) \
V(util) \
V(pipe_wrap) \
V(sea) \
Expand Down
Loading

0 comments on commit 9659662

Please sign in to comment.