mirror of
https://github.com/zebrajr/node.git
synced 2025-12-06 00:20:08 +01:00
url: add URLPattern implementation
Co-authored-by: Daniel Lemire <daniel@lemire.me> PR-URL: https://github.com/nodejs/node/pull/56452 Reviewed-By: Daniel Lemire <daniel@lemire.me> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Jordan Harband <ljharb@gmail.com> Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
This commit is contained in:
parent
063e67c2ff
commit
43c593c428
|
|
@ -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`
|
||||
|
|
|
|||
123
doc/api/url.md
123
doc/api/url.md
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ const {
|
|||
decodeURIComponent,
|
||||
} = primordials;
|
||||
|
||||
const { URLPattern } = internalBinding('url_pattern');
|
||||
const { inspect } = require('internal/util/inspect');
|
||||
const {
|
||||
encodeStr,
|
||||
|
|
@ -1574,6 +1575,7 @@ module.exports = {
|
|||
toPathIfFileURL,
|
||||
installObjectURLMethods,
|
||||
URL,
|
||||
URLPattern,
|
||||
URLSearchParams,
|
||||
URLParse: URL.parse,
|
||||
domainToASCII,
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
@ -1029,6 +1030,7 @@ module.exports = {
|
|||
|
||||
// WHATWG API
|
||||
URL,
|
||||
URLPattern,
|
||||
URLSearchParams,
|
||||
domainToASCII,
|
||||
domainToUnicode,
|
||||
|
|
|
|||
2
node.gyp
2
node.gyp
|
|
@ -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',
|
||||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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") \
|
||||
|
|
@ -179,6 +180,9 @@
|
|||
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") \
|
||||
|
|
@ -186,13 +190,16 @@
|
|||
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") \
|
||||
|
|
@ -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") \
|
||||
|
|
@ -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") \
|
||||
|
|
@ -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") \
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -87,6 +88,7 @@
|
|||
V(types) \
|
||||
V(udp_wrap) \
|
||||
V(url) \
|
||||
V(url_pattern) \
|
||||
V(util) \
|
||||
V(uv) \
|
||||
V(v8) \
|
||||
|
|
|
|||
|
|
@ -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) \
|
||||
|
|
@ -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) \
|
||||
|
|
|
|||
|
|
@ -180,6 +180,7 @@ class ExternalReferenceRegistry {
|
|||
V(tty_wrap) \
|
||||
V(udp_wrap) \
|
||||
V(url) \
|
||||
V(url_pattern) \
|
||||
V(util) \
|
||||
V(pipe_wrap) \
|
||||
V(sea) \
|
||||
|
|
|
|||
787
src/node_url_pattern.cc
Normal file
787
src/node_url_pattern.cc
Normal file
|
|
@ -0,0 +1,787 @@
|
|||
#include "node_url_pattern.h"
|
||||
#include "base_object-inl.h"
|
||||
#include "debug_utils-inl.h"
|
||||
#include "env-inl.h"
|
||||
#include "node.h"
|
||||
#include "node_errors.h"
|
||||
#include "node_mem-inl.h"
|
||||
#include "path.h"
|
||||
#include "util-inl.h"
|
||||
|
||||
namespace node::url_pattern {
|
||||
|
||||
using v8::Array;
|
||||
using v8::Context;
|
||||
using v8::DontDelete;
|
||||
using v8::FunctionCallbackInfo;
|
||||
using v8::FunctionTemplate;
|
||||
using v8::Global;
|
||||
using v8::Isolate;
|
||||
using v8::Local;
|
||||
using v8::LocalVector;
|
||||
using v8::MaybeLocal;
|
||||
using v8::Name;
|
||||
using v8::NewStringType;
|
||||
using v8::Object;
|
||||
using v8::PropertyAttribute;
|
||||
using v8::ReadOnly;
|
||||
using v8::RegExp;
|
||||
using v8::String;
|
||||
using v8::Value;
|
||||
|
||||
std::optional<URLPatternRegexProvider::regex_type>
|
||||
URLPatternRegexProvider::create_instance(std::string_view pattern,
|
||||
bool ignore_case) {
|
||||
auto isolate = Isolate::GetCurrent();
|
||||
auto env = Environment::GetCurrent(isolate);
|
||||
int flags = RegExp::Flags::kUnicodeSets;
|
||||
if (ignore_case) {
|
||||
flags |= static_cast<int>(RegExp::Flags::kIgnoreCase);
|
||||
}
|
||||
|
||||
Local<String> local_pattern;
|
||||
if (!String::NewFromUtf8(
|
||||
isolate, pattern.data(), NewStringType::kNormal, pattern.size())
|
||||
.ToLocal(&local_pattern)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
Local<RegExp> regexp;
|
||||
if (!RegExp::New(
|
||||
env->context(), local_pattern, static_cast<RegExp::Flags>(flags))
|
||||
.ToLocal(®exp)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return Global<RegExp>(isolate, regexp);
|
||||
}
|
||||
|
||||
bool URLPatternRegexProvider::regex_match(std::string_view input,
|
||||
const regex_type& pattern) {
|
||||
auto isolate = Isolate::GetCurrent();
|
||||
auto env = Environment::GetCurrent(isolate);
|
||||
Local<String> local_input;
|
||||
if (!String::NewFromUtf8(
|
||||
isolate, input.data(), NewStringType::kNormal, input.size())
|
||||
.ToLocal(&local_input)) {
|
||||
return false;
|
||||
}
|
||||
Local<Object> result_object;
|
||||
if (!pattern.Get(isolate)
|
||||
->Exec(env->context(), local_input)
|
||||
.ToLocal(&result_object)) {
|
||||
return false;
|
||||
}
|
||||
// RegExp::Exec returns null if there is no match.
|
||||
return !result_object->IsNull();
|
||||
}
|
||||
|
||||
std::optional<std::vector<std::optional<std::string>>>
|
||||
URLPatternRegexProvider::regex_search(std::string_view input,
|
||||
const regex_type& global_pattern) {
|
||||
auto isolate = Isolate::GetCurrent();
|
||||
auto env = Environment::GetCurrent(isolate);
|
||||
Local<String> local_input;
|
||||
if (!String::NewFromUtf8(
|
||||
isolate, input.data(), NewStringType::kNormal, input.size())
|
||||
.ToLocal(&local_input)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
Local<Object> exec_result_object;
|
||||
auto pattern = global_pattern.Get(isolate);
|
||||
if (!pattern->Exec(env->context(), local_input)
|
||||
.ToLocal(&exec_result_object) ||
|
||||
exec_result_object->IsNull()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
DCHECK(exec_result_object->IsArray());
|
||||
auto exec_result = exec_result_object.As<Array>();
|
||||
size_t len = exec_result->Length();
|
||||
std::vector<std::optional<std::string>> result;
|
||||
result.reserve(len);
|
||||
for (size_t i = 1; i < len; i++) {
|
||||
Local<Value> entry;
|
||||
if (!exec_result->Get(env->context(), i).ToLocal(&entry)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (entry->IsUndefined()) {
|
||||
result.emplace_back(std::nullopt);
|
||||
} else if (entry->IsString()) {
|
||||
Utf8Value utf8_entry(isolate, entry.As<String>());
|
||||
result.emplace_back(utf8_entry.ToString());
|
||||
} else {
|
||||
UNREACHABLE("v8::RegExp::Exec return a non-string, non-undefined value.");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
URLPattern::URLPattern(Environment* env,
|
||||
Local<Object> object,
|
||||
ada::url_pattern<URLPatternRegexProvider>&& url_pattern)
|
||||
: BaseObject(env, object), url_pattern_(std::move(url_pattern)) {
|
||||
MakeWeak();
|
||||
}
|
||||
|
||||
void URLPattern::MemoryInfo(MemoryTracker* tracker) const {
|
||||
tracker->TrackFieldWithSize("protocol", url_pattern_.get_protocol().size());
|
||||
tracker->TrackFieldWithSize("username", url_pattern_.get_username().size());
|
||||
tracker->TrackFieldWithSize("password", url_pattern_.get_password().size());
|
||||
tracker->TrackFieldWithSize("hostname", url_pattern_.get_hostname().size());
|
||||
tracker->TrackFieldWithSize("pathname", url_pattern_.get_pathname().size());
|
||||
tracker->TrackFieldWithSize("search", url_pattern_.get_search().size());
|
||||
tracker->TrackFieldWithSize("hash", url_pattern_.get_hash().size());
|
||||
}
|
||||
|
||||
void URLPattern::New(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
|
||||
if (!args.IsConstructCall()) {
|
||||
THROW_ERR_CONSTRUCT_CALL_REQUIRED(env);
|
||||
return;
|
||||
}
|
||||
|
||||
// If no arguments are passed, then we parse the empty URLPattern.
|
||||
if (args.Length() == 0) {
|
||||
auto init = ada::url_pattern_init{};
|
||||
auto url_pattern = ada::parse_url_pattern<URLPatternRegexProvider>(init);
|
||||
CHECK(url_pattern);
|
||||
new URLPattern(env, args.This(), std::move(*url_pattern));
|
||||
return;
|
||||
}
|
||||
|
||||
std::optional<ada::url_pattern_init> init{};
|
||||
std::optional<std::string> input{};
|
||||
std::optional<std::string> base_url{};
|
||||
std::optional<ada::url_pattern_options> options{};
|
||||
|
||||
// Following patterns are supported:
|
||||
// - new URLPattern(input)
|
||||
// - new URLPattern(input, baseURL)
|
||||
// - new URLPattern(input, options)
|
||||
// - new URLPattern(input, baseURL, options)
|
||||
if (args[0]->IsString()) {
|
||||
BufferValue input_buffer(env->isolate(), args[0]);
|
||||
CHECK_NOT_NULL(*input_buffer);
|
||||
input = input_buffer.ToString();
|
||||
} else if (args[0]->IsObject()) {
|
||||
init = URLPatternInit::FromJsObject(env, args[0].As<Object>());
|
||||
} else {
|
||||
THROW_ERR_INVALID_ARG_TYPE(env, "Input must be an object or a string");
|
||||
return;
|
||||
}
|
||||
|
||||
// The next argument can be baseURL or options.
|
||||
if (args.Length() > 1) {
|
||||
if (args[1]->IsString()) {
|
||||
BufferValue base_url_buffer(env->isolate(), args[1]);
|
||||
CHECK_NOT_NULL(*base_url_buffer);
|
||||
base_url = base_url_buffer.ToString();
|
||||
} else if (args[1]->IsObject()) {
|
||||
CHECK(!options.has_value());
|
||||
options = URLPatternOptions::FromJsObject(env, args[1].As<Object>());
|
||||
if (!options) {
|
||||
THROW_ERR_INVALID_ARG_TYPE(env, "options.ignoreCase must be a boolean");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
THROW_ERR_MISSING_ARGS(env, "baseURL or options must be provided");
|
||||
return;
|
||||
}
|
||||
|
||||
// Only remaining argument can be options.
|
||||
if (args.Length() > 2) {
|
||||
if (!args[2]->IsObject()) {
|
||||
THROW_ERR_INVALID_ARG_TYPE(env, "options must be an object");
|
||||
return;
|
||||
}
|
||||
CHECK(!options.has_value());
|
||||
options = URLPatternOptions::FromJsObject(env, args[2].As<Object>());
|
||||
if (!options) {
|
||||
THROW_ERR_INVALID_ARG_TYPE(env, "options.ignoreCase must be a boolean");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Either url_pattern_init or input as a string must be provided.
|
||||
CHECK_IMPLIES(init.has_value(), !input.has_value());
|
||||
|
||||
std::string_view base_url_view{};
|
||||
if (base_url) base_url_view = {base_url->data(), base_url->size()};
|
||||
|
||||
ada::url_pattern_input arg0;
|
||||
if (init.has_value()) {
|
||||
arg0 = std::move(*init);
|
||||
} else {
|
||||
arg0 = std::move(*input);
|
||||
}
|
||||
|
||||
auto url_pattern = parse_url_pattern<URLPatternRegexProvider>(
|
||||
arg0,
|
||||
base_url ? &base_url_view : nullptr,
|
||||
options.has_value() ? &options.value() : nullptr);
|
||||
|
||||
if (!url_pattern) {
|
||||
THROW_ERR_INVALID_URL_PATTERN(env, "Failed to constuct URLPattern");
|
||||
return;
|
||||
}
|
||||
|
||||
new URLPattern(env, args.This(), std::move(*url_pattern));
|
||||
}
|
||||
|
||||
MaybeLocal<Value> URLPattern::URLPatternInit::ToJsObject(
|
||||
Environment* env, const ada::url_pattern_init& init) {
|
||||
auto isolate = env->isolate();
|
||||
auto context = env->context();
|
||||
auto result = Object::New(isolate);
|
||||
if (init.protocol) {
|
||||
Local<Value> protocol;
|
||||
if (!ToV8Value(context, *init.protocol).ToLocal(&protocol) ||
|
||||
result->Set(context, env->protocol_string(), protocol).IsNothing()) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
if (init.username) {
|
||||
Local<Value> username;
|
||||
if (!ToV8Value(context, *init.username).ToLocal(&username) ||
|
||||
result->Set(context, env->username_string(), username).IsNothing()) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
if (init.password) {
|
||||
Local<Value> password;
|
||||
if (!ToV8Value(context, *init.password).ToLocal(&password) ||
|
||||
result->Set(context, env->password_string(), password).IsNothing()) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
if (init.hostname) {
|
||||
Local<Value> hostname;
|
||||
if (!ToV8Value(context, *init.hostname).ToLocal(&hostname) ||
|
||||
result->Set(context, env->hostname_string(), hostname).IsNothing()) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
if (init.port) {
|
||||
Local<Value> port;
|
||||
if (!ToV8Value(context, *init.port).ToLocal(&port) ||
|
||||
result->Set(context, env->port_string(), port).IsNothing()) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
if (init.pathname) {
|
||||
Local<Value> pathname;
|
||||
if (!ToV8Value(context, *init.pathname).ToLocal(&pathname) ||
|
||||
result->Set(context, env->pathname_string(), pathname).IsNothing()) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
if (init.search) {
|
||||
Local<Value> search;
|
||||
if (!ToV8Value(context, *init.search).ToLocal(&search) ||
|
||||
result->Set(context, env->search_string(), search).IsNothing()) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
if (init.hash) {
|
||||
Local<Value> hash;
|
||||
if (!ToV8Value(context, *init.hash).ToLocal(&hash) ||
|
||||
result->Set(context, env->hash_string(), hash).IsNothing()) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
if (init.base_url) {
|
||||
Local<Value> base_url;
|
||||
if (!ToV8Value(context, *init.base_url).ToLocal(&base_url) ||
|
||||
result->Set(context, env->base_url_string(), base_url).IsNothing()) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ada::url_pattern_init URLPattern::URLPatternInit::FromJsObject(
|
||||
Environment* env, Local<Object> obj) {
|
||||
ada::url_pattern_init init{};
|
||||
Local<String> components[] = {
|
||||
env->protocol_string(),
|
||||
env->username_string(),
|
||||
env->password_string(),
|
||||
env->hostname_string(),
|
||||
env->port_string(),
|
||||
env->pathname_string(),
|
||||
env->search_string(),
|
||||
env->hash_string(),
|
||||
env->base_url_string(),
|
||||
};
|
||||
auto isolate = env->isolate();
|
||||
const auto set_parameter = [&](std::string_view key, std::string_view value) {
|
||||
if (key == "protocol") {
|
||||
init.protocol = std::string(value);
|
||||
} else if (key == "username") {
|
||||
init.username = std::string(value);
|
||||
} else if (key == "password") {
|
||||
init.password = std::string(value);
|
||||
} else if (key == "hostname") {
|
||||
init.hostname = std::string(value);
|
||||
} else if (key == "port") {
|
||||
init.port = std::string(value);
|
||||
} else if (key == "pathname") {
|
||||
init.pathname = std::string(value);
|
||||
} else if (key == "search") {
|
||||
init.search = std::string(value);
|
||||
} else if (key == "hash") {
|
||||
init.hash = std::string(value);
|
||||
} else if (key == "baseURL") {
|
||||
init.base_url = std::string(value);
|
||||
}
|
||||
};
|
||||
Local<Value> value;
|
||||
for (const auto& component : components) {
|
||||
Utf8Value key(isolate, component);
|
||||
if (obj->Get(env->context(), component).ToLocal(&value)) {
|
||||
if (value->IsString()) {
|
||||
Utf8Value utf8_value(isolate, value);
|
||||
set_parameter(key.ToStringView(), utf8_value.ToStringView());
|
||||
}
|
||||
}
|
||||
}
|
||||
return init;
|
||||
}
|
||||
|
||||
MaybeLocal<Object> URLPattern::URLPatternComponentResult::ToJSObject(
|
||||
Environment* env, const ada::url_pattern_component_result& result) {
|
||||
auto isolate = env->isolate();
|
||||
auto context = env->context();
|
||||
auto parsed_group = Object::New(isolate);
|
||||
for (const auto& [group_key, group_value] : result.groups) {
|
||||
Local<String> key;
|
||||
if (!String::NewFromUtf8(isolate,
|
||||
group_key.c_str(),
|
||||
NewStringType::kNormal,
|
||||
group_key.size())
|
||||
.ToLocal(&key)) {
|
||||
return {};
|
||||
}
|
||||
Local<Value> value;
|
||||
if (group_value) {
|
||||
if (!ToV8Value(env->context(), *group_value).ToLocal(&value)) {
|
||||
return {};
|
||||
}
|
||||
} else {
|
||||
value = Undefined(isolate);
|
||||
}
|
||||
USE(parsed_group->Set(context, key, value));
|
||||
}
|
||||
Local<Value> input;
|
||||
if (!ToV8Value(env->context(), result.input).ToLocal(&input)) {
|
||||
return {};
|
||||
}
|
||||
Local<Name> names[] = {env->input_string(), env->groups_string()};
|
||||
Local<Value> values[] = {input, parsed_group};
|
||||
DCHECK_EQ(arraysize(names), arraysize(values));
|
||||
return Object::New(
|
||||
isolate, Object::New(isolate), names, values, arraysize(names));
|
||||
}
|
||||
|
||||
MaybeLocal<Value> URLPattern::URLPatternResult::ToJSValue(
|
||||
Environment* env, const ada::url_pattern_result& result) {
|
||||
auto isolate = env->isolate();
|
||||
Local<Name> names[] = {
|
||||
env->inputs_string(),
|
||||
env->protocol_string(),
|
||||
env->username_string(),
|
||||
env->password_string(),
|
||||
env->hostname_string(),
|
||||
env->port_string(),
|
||||
env->pathname_string(),
|
||||
env->search_string(),
|
||||
env->hash_string(),
|
||||
};
|
||||
LocalVector<Value> inputs(isolate, result.inputs.size());
|
||||
size_t index = 0;
|
||||
for (auto& input : result.inputs) {
|
||||
if (std::holds_alternative<std::string_view>(input)) {
|
||||
auto input_str = std::get<std::string_view>(input);
|
||||
if (!ToV8Value(env->context(), input_str).ToLocal(&inputs[index])) {
|
||||
return {};
|
||||
}
|
||||
} else {
|
||||
DCHECK(std::holds_alternative<ada::url_pattern_init>(input));
|
||||
auto init = std::get<ada::url_pattern_init>(input);
|
||||
if (!URLPatternInit::ToJsObject(env, init).ToLocal(&inputs[index])) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
index++;
|
||||
}
|
||||
LocalVector<Value> values(isolate, arraysize(names));
|
||||
values[0] = Array::New(isolate, inputs.data(), inputs.size());
|
||||
if (!URLPatternComponentResult::ToJSObject(env, result.protocol)
|
||||
.ToLocal(&values[1])) {
|
||||
return {};
|
||||
}
|
||||
if (!URLPatternComponentResult::ToJSObject(env, result.username)
|
||||
.ToLocal(&values[2])) {
|
||||
return {};
|
||||
}
|
||||
if (!URLPatternComponentResult::ToJSObject(env, result.password)
|
||||
.ToLocal(&values[3])) {
|
||||
return {};
|
||||
}
|
||||
if (!URLPatternComponentResult::ToJSObject(env, result.hostname)
|
||||
.ToLocal(&values[4])) {
|
||||
return {};
|
||||
}
|
||||
if (!URLPatternComponentResult::ToJSObject(env, result.port)
|
||||
.ToLocal(&values[5])) {
|
||||
return {};
|
||||
}
|
||||
if (!URLPatternComponentResult::ToJSObject(env, result.pathname)
|
||||
.ToLocal(&values[6])) {
|
||||
return {};
|
||||
}
|
||||
if (!URLPatternComponentResult::ToJSObject(env, result.search)
|
||||
.ToLocal(&values[7])) {
|
||||
return {};
|
||||
}
|
||||
if (!URLPatternComponentResult::ToJSObject(env, result.hash)
|
||||
.ToLocal(&values[8])) {
|
||||
return {};
|
||||
}
|
||||
return Object::New(
|
||||
isolate, Object::New(isolate), names, values.data(), values.size());
|
||||
}
|
||||
|
||||
std::optional<ada::url_pattern_options>
|
||||
URLPattern::URLPatternOptions::FromJsObject(Environment* env,
|
||||
Local<Object> obj) {
|
||||
ada::url_pattern_options options{};
|
||||
Local<Value> ignore_case;
|
||||
if (obj->Get(env->context(), env->ignore_case_string())
|
||||
.ToLocal(&ignore_case)) {
|
||||
if (!ignore_case->IsBoolean()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
options.ignore_case = ignore_case->IsTrue();
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
MaybeLocal<Value> URLPattern::Hash() const {
|
||||
auto context = env()->context();
|
||||
return ToV8Value(context, url_pattern_.get_hash());
|
||||
}
|
||||
|
||||
MaybeLocal<Value> URLPattern::Hostname() const {
|
||||
auto context = env()->context();
|
||||
return ToV8Value(context, url_pattern_.get_hostname());
|
||||
}
|
||||
|
||||
MaybeLocal<Value> URLPattern::Password() const {
|
||||
auto context = env()->context();
|
||||
return ToV8Value(context, url_pattern_.get_password());
|
||||
}
|
||||
|
||||
MaybeLocal<Value> URLPattern::Pathname() const {
|
||||
auto context = env()->context();
|
||||
return ToV8Value(context, url_pattern_.get_pathname());
|
||||
}
|
||||
|
||||
MaybeLocal<Value> URLPattern::Port() const {
|
||||
auto context = env()->context();
|
||||
return ToV8Value(context, url_pattern_.get_port());
|
||||
}
|
||||
|
||||
MaybeLocal<Value> URLPattern::Protocol() const {
|
||||
auto context = env()->context();
|
||||
return ToV8Value(context, url_pattern_.get_protocol());
|
||||
}
|
||||
|
||||
MaybeLocal<Value> URLPattern::Search() const {
|
||||
auto context = env()->context();
|
||||
return ToV8Value(context, url_pattern_.get_search());
|
||||
}
|
||||
|
||||
MaybeLocal<Value> URLPattern::Username() const {
|
||||
auto context = env()->context();
|
||||
return ToV8Value(context, url_pattern_.get_username());
|
||||
}
|
||||
|
||||
bool URLPattern::HasRegExpGroups() const {
|
||||
return url_pattern_.has_regexp_groups();
|
||||
}
|
||||
|
||||
// Instance methods
|
||||
|
||||
MaybeLocal<Value> URLPattern::Exec(Environment* env,
|
||||
const ada::url_pattern_input& input,
|
||||
std::optional<std::string_view>& baseURL) {
|
||||
if (auto result = url_pattern_.exec(input, baseURL ? &*baseURL : nullptr)) {
|
||||
if (result->has_value()) {
|
||||
return URLPatternResult::ToJSValue(env, result->value());
|
||||
}
|
||||
return Null(env->isolate());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool URLPattern::Test(Environment* env,
|
||||
const ada::url_pattern_input& input,
|
||||
std::optional<std::string_view>& baseURL) {
|
||||
if (auto result = url_pattern_.test(input, baseURL ? &*baseURL : nullptr)) {
|
||||
return *result;
|
||||
}
|
||||
THROW_ERR_OPERATION_FAILED(env, "Failed to test URLPattern");
|
||||
return false;
|
||||
}
|
||||
|
||||
// V8 Methods
|
||||
|
||||
void URLPattern::Exec(const FunctionCallbackInfo<Value>& args) {
|
||||
URLPattern* url_pattern;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&url_pattern, args.This());
|
||||
auto env = Environment::GetCurrent(args);
|
||||
|
||||
ada::url_pattern_input input;
|
||||
std::optional<std::string> baseURL{};
|
||||
std::string input_base;
|
||||
if (args.Length() == 0) {
|
||||
input = ada::url_pattern_init{};
|
||||
} else if (args[0]->IsString()) {
|
||||
Utf8Value input_value(env->isolate(), args[0].As<String>());
|
||||
input_base = input_value.ToString();
|
||||
input = std::string_view(input_base);
|
||||
} else if (args[0]->IsObject()) {
|
||||
input = URLPatternInit::FromJsObject(env, args[0].As<Object>());
|
||||
} else {
|
||||
THROW_ERR_INVALID_ARG_TYPE(
|
||||
env, "URLPattern input needs to be a string or an object");
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Length() > 1) {
|
||||
if (!args[1]->IsString()) {
|
||||
THROW_ERR_INVALID_ARG_TYPE(env, "baseURL must be a string");
|
||||
return;
|
||||
}
|
||||
Utf8Value base_url_value(env->isolate(), args[1].As<String>());
|
||||
baseURL = base_url_value.ToStringView();
|
||||
}
|
||||
|
||||
Local<Value> result;
|
||||
std::optional<std::string_view> baseURL_opt =
|
||||
baseURL ? std::optional<std::string_view>(*baseURL) : std::nullopt;
|
||||
if (!url_pattern->Exec(env, input, baseURL_opt).ToLocal(&result)) {
|
||||
THROW_ERR_OPERATION_FAILED(env, "Failed to exec URLPattern");
|
||||
return;
|
||||
}
|
||||
args.GetReturnValue().Set(result);
|
||||
}
|
||||
|
||||
void URLPattern::Test(const FunctionCallbackInfo<Value>& args) {
|
||||
URLPattern* url_pattern;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&url_pattern, args.This());
|
||||
auto env = Environment::GetCurrent(args);
|
||||
|
||||
ada::url_pattern_input input;
|
||||
std::optional<std::string> baseURL{};
|
||||
std::string input_base;
|
||||
if (args.Length() == 0) {
|
||||
input = ada::url_pattern_init{};
|
||||
} else if (args[0]->IsString()) {
|
||||
Utf8Value input_value(env->isolate(), args[0].As<String>());
|
||||
input_base = input_value.ToString();
|
||||
input = std::string_view(input_base);
|
||||
} else if (args[0]->IsObject()) {
|
||||
input = URLPatternInit::FromJsObject(env, args[0].As<Object>());
|
||||
} else {
|
||||
THROW_ERR_INVALID_ARG_TYPE(
|
||||
env, "URLPattern input needs to be a string or an object");
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Length() > 1) {
|
||||
if (!args[1]->IsString()) {
|
||||
THROW_ERR_INVALID_ARG_TYPE(env, "baseURL must be a string");
|
||||
return;
|
||||
}
|
||||
Utf8Value base_url_value(env->isolate(), args[1].As<String>());
|
||||
baseURL = base_url_value.ToStringView();
|
||||
}
|
||||
|
||||
std::optional<std::string_view> baseURL_opt =
|
||||
baseURL ? std::optional<std::string_view>(*baseURL) : std::nullopt;
|
||||
args.GetReturnValue().Set(url_pattern->Test(env, input, baseURL_opt));
|
||||
}
|
||||
|
||||
void URLPattern::Protocol(const FunctionCallbackInfo<Value>& info) {
|
||||
URLPattern* url_pattern;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This());
|
||||
Local<Value> result;
|
||||
if (url_pattern->Protocol().ToLocal(&result)) {
|
||||
info.GetReturnValue().Set(result);
|
||||
}
|
||||
}
|
||||
|
||||
void URLPattern::Username(const FunctionCallbackInfo<Value>& info) {
|
||||
URLPattern* url_pattern;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This());
|
||||
Local<Value> result;
|
||||
if (url_pattern->Username().ToLocal(&result)) {
|
||||
info.GetReturnValue().Set(result);
|
||||
}
|
||||
}
|
||||
|
||||
void URLPattern::Password(const FunctionCallbackInfo<Value>& info) {
|
||||
URLPattern* url_pattern;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This());
|
||||
Local<Value> result;
|
||||
if (url_pattern->Password().ToLocal(&result)) {
|
||||
info.GetReturnValue().Set(result);
|
||||
}
|
||||
}
|
||||
|
||||
void URLPattern::Hostname(const FunctionCallbackInfo<Value>& info) {
|
||||
URLPattern* url_pattern;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This());
|
||||
Local<Value> result;
|
||||
if (url_pattern->Hostname().ToLocal(&result)) {
|
||||
info.GetReturnValue().Set(result);
|
||||
}
|
||||
}
|
||||
|
||||
void URLPattern::Port(const FunctionCallbackInfo<Value>& info) {
|
||||
URLPattern* url_pattern;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This());
|
||||
Local<Value> result;
|
||||
if (url_pattern->Port().ToLocal(&result)) {
|
||||
info.GetReturnValue().Set(result);
|
||||
}
|
||||
}
|
||||
|
||||
void URLPattern::Pathname(const FunctionCallbackInfo<Value>& info) {
|
||||
URLPattern* url_pattern;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This());
|
||||
Local<Value> result;
|
||||
if (url_pattern->Pathname().ToLocal(&result)) {
|
||||
info.GetReturnValue().Set(result);
|
||||
}
|
||||
}
|
||||
|
||||
void URLPattern::Search(const FunctionCallbackInfo<Value>& info) {
|
||||
URLPattern* url_pattern;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This());
|
||||
Local<Value> result;
|
||||
if (url_pattern->Search().ToLocal(&result)) {
|
||||
info.GetReturnValue().Set(result);
|
||||
}
|
||||
}
|
||||
|
||||
void URLPattern::Hash(const FunctionCallbackInfo<Value>& info) {
|
||||
URLPattern* url_pattern;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This());
|
||||
Local<Value> result;
|
||||
if (url_pattern->Hash().ToLocal(&result)) {
|
||||
info.GetReturnValue().Set(result);
|
||||
}
|
||||
}
|
||||
|
||||
void URLPattern::HasRegexpGroups(const FunctionCallbackInfo<Value>& info) {
|
||||
URLPattern* url_pattern;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This());
|
||||
info.GetReturnValue().Set(url_pattern->HasRegExpGroups());
|
||||
}
|
||||
|
||||
static void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
|
||||
registry->Register(URLPattern::New);
|
||||
registry->Register(URLPattern::Protocol);
|
||||
registry->Register(URLPattern::Username);
|
||||
registry->Register(URLPattern::Password);
|
||||
registry->Register(URLPattern::Hostname);
|
||||
registry->Register(URLPattern::Port);
|
||||
registry->Register(URLPattern::Pathname);
|
||||
registry->Register(URLPattern::Search);
|
||||
registry->Register(URLPattern::Hash);
|
||||
registry->Register(URLPattern::HasRegexpGroups);
|
||||
registry->Register(URLPattern::Exec);
|
||||
registry->Register(URLPattern::Test);
|
||||
}
|
||||
|
||||
static void Initialize(Local<Object> target,
|
||||
Local<Value> unused,
|
||||
Local<Context> context,
|
||||
void* priv) {
|
||||
Environment* env = Environment::GetCurrent(context);
|
||||
Isolate* isolate = env->isolate();
|
||||
auto attributes = static_cast<PropertyAttribute>(ReadOnly | DontDelete);
|
||||
auto ctor_tmpl = NewFunctionTemplate(isolate, URLPattern::New);
|
||||
auto instance_template = ctor_tmpl->InstanceTemplate();
|
||||
auto prototype_template = ctor_tmpl->PrototypeTemplate();
|
||||
ctor_tmpl->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "URLPattern"));
|
||||
|
||||
instance_template->SetInternalFieldCount(URLPattern::kInternalFieldCount);
|
||||
prototype_template->SetAccessorProperty(
|
||||
env->protocol_string(),
|
||||
FunctionTemplate::New(isolate, URLPattern::Protocol),
|
||||
Local<FunctionTemplate>(),
|
||||
attributes);
|
||||
|
||||
prototype_template->SetAccessorProperty(
|
||||
env->username_string(),
|
||||
FunctionTemplate::New(isolate, URLPattern::Username),
|
||||
Local<FunctionTemplate>(),
|
||||
attributes);
|
||||
|
||||
prototype_template->SetAccessorProperty(
|
||||
env->password_string(),
|
||||
FunctionTemplate::New(isolate, URLPattern::Password),
|
||||
Local<FunctionTemplate>(),
|
||||
attributes);
|
||||
|
||||
prototype_template->SetAccessorProperty(
|
||||
env->hostname_string(),
|
||||
FunctionTemplate::New(isolate, URLPattern::Hostname),
|
||||
Local<FunctionTemplate>(),
|
||||
attributes);
|
||||
|
||||
prototype_template->SetAccessorProperty(
|
||||
env->port_string(),
|
||||
FunctionTemplate::New(isolate, URLPattern::Port),
|
||||
Local<FunctionTemplate>(),
|
||||
attributes);
|
||||
|
||||
prototype_template->SetAccessorProperty(
|
||||
env->pathname_string(),
|
||||
FunctionTemplate::New(isolate, URLPattern::Pathname),
|
||||
Local<FunctionTemplate>(),
|
||||
attributes);
|
||||
|
||||
prototype_template->SetAccessorProperty(
|
||||
env->search_string(),
|
||||
FunctionTemplate::New(isolate, URLPattern::Search),
|
||||
Local<FunctionTemplate>(),
|
||||
attributes);
|
||||
|
||||
prototype_template->SetAccessorProperty(
|
||||
env->hash_string(),
|
||||
FunctionTemplate::New(isolate, URLPattern::Hash),
|
||||
Local<FunctionTemplate>(),
|
||||
attributes);
|
||||
|
||||
prototype_template->SetAccessorProperty(
|
||||
env->has_regexp_groups_string(),
|
||||
FunctionTemplate::New(isolate, URLPattern::HasRegexpGroups),
|
||||
Local<FunctionTemplate>(),
|
||||
attributes);
|
||||
|
||||
SetProtoMethodNoSideEffect(isolate, ctor_tmpl, "exec", URLPattern::Exec);
|
||||
SetProtoMethodNoSideEffect(isolate, ctor_tmpl, "test", URLPattern::Test);
|
||||
SetConstructorFunction(context, target, "URLPattern", ctor_tmpl);
|
||||
}
|
||||
|
||||
} // namespace node::url_pattern
|
||||
|
||||
NODE_BINDING_CONTEXT_AWARE_INTERNAL(url_pattern, node::url_pattern::Initialize)
|
||||
NODE_BINDING_EXTERNAL_REFERENCE(url_pattern,
|
||||
node::url_pattern::RegisterExternalReferences)
|
||||
112
src/node_url_pattern.h
Normal file
112
src/node_url_pattern.h
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
#ifndef SRC_NODE_URL_PATTERN_H_
|
||||
#define SRC_NODE_URL_PATTERN_H_
|
||||
|
||||
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||
|
||||
#include "ada.h"
|
||||
#include "base_object.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <v8.h>
|
||||
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
namespace node::url_pattern {
|
||||
|
||||
// By default, ada::url_pattern doesn't ship with any regex library.
|
||||
// Ada has a std::regex implementation, but it is considered unsafe and does
|
||||
// not have a fully compliant ecmascript syntax support. Therefore, Ada
|
||||
// supports passing custom regex provider that conforms to the following
|
||||
// class and function structure. For more information, please look into
|
||||
// url_pattern_regex.h inside github.com/ada-url/ada.
|
||||
class URLPatternRegexProvider {
|
||||
public:
|
||||
using regex_type = v8::Global<v8::RegExp>;
|
||||
static std::optional<regex_type> create_instance(std::string_view pattern,
|
||||
bool ignore_case);
|
||||
static std::optional<std::vector<std::optional<std::string>>> regex_search(
|
||||
std::string_view input, const regex_type& pattern);
|
||||
static bool regex_match(std::string_view input, const regex_type& pattern);
|
||||
};
|
||||
|
||||
class URLPattern : public BaseObject {
|
||||
public:
|
||||
URLPattern(Environment* env,
|
||||
v8::Local<v8::Object> object,
|
||||
ada::url_pattern<URLPatternRegexProvider>&& url_pattern);
|
||||
|
||||
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
// V8 APIs
|
||||
// - Functions
|
||||
static void Exec(const v8::FunctionCallbackInfo<v8::Value>& info);
|
||||
static void Test(const v8::FunctionCallbackInfo<v8::Value>& info);
|
||||
// - Getters
|
||||
static void Hash(const v8::FunctionCallbackInfo<v8::Value>& info);
|
||||
static void Hostname(const v8::FunctionCallbackInfo<v8::Value>& info);
|
||||
static void Password(const v8::FunctionCallbackInfo<v8::Value>& info);
|
||||
static void Pathname(const v8::FunctionCallbackInfo<v8::Value>& info);
|
||||
static void Port(const v8::FunctionCallbackInfo<v8::Value>& info);
|
||||
static void Protocol(const v8::FunctionCallbackInfo<v8::Value>& info);
|
||||
static void Search(const v8::FunctionCallbackInfo<v8::Value>& info);
|
||||
static void Username(const v8::FunctionCallbackInfo<v8::Value>& info);
|
||||
static void HasRegexpGroups(const v8::FunctionCallbackInfo<v8::Value>& info);
|
||||
|
||||
void MemoryInfo(MemoryTracker* tracker) const override;
|
||||
SET_MEMORY_INFO_NAME(URLPattern)
|
||||
SET_SELF_SIZE(URLPattern)
|
||||
|
||||
class URLPatternInit {
|
||||
public:
|
||||
static ada::url_pattern_init FromJsObject(Environment* env,
|
||||
v8::Local<v8::Object> obj);
|
||||
static v8::MaybeLocal<v8::Value> ToJsObject(
|
||||
Environment* env, const ada::url_pattern_init& init);
|
||||
};
|
||||
|
||||
class URLPatternOptions {
|
||||
public:
|
||||
static std::optional<ada::url_pattern_options> FromJsObject(
|
||||
Environment* env, v8::Local<v8::Object> obj);
|
||||
};
|
||||
|
||||
class URLPatternResult {
|
||||
public:
|
||||
static v8::MaybeLocal<v8::Value> ToJSValue(
|
||||
Environment* env, const ada::url_pattern_result& result);
|
||||
};
|
||||
|
||||
class URLPatternComponentResult {
|
||||
public:
|
||||
static v8::MaybeLocal<v8::Object> ToJSObject(
|
||||
Environment* env, const ada::url_pattern_component_result& result);
|
||||
};
|
||||
|
||||
private:
|
||||
ada::url_pattern<URLPatternRegexProvider> url_pattern_;
|
||||
// Getter methods
|
||||
v8::MaybeLocal<v8::Value> Hash() const;
|
||||
v8::MaybeLocal<v8::Value> Hostname() const;
|
||||
v8::MaybeLocal<v8::Value> Password() const;
|
||||
v8::MaybeLocal<v8::Value> Pathname() const;
|
||||
v8::MaybeLocal<v8::Value> Port() const;
|
||||
v8::MaybeLocal<v8::Value> Protocol() const;
|
||||
v8::MaybeLocal<v8::Value> Search() const;
|
||||
v8::MaybeLocal<v8::Value> Username() const;
|
||||
bool HasRegExpGroups() const;
|
||||
// Public API
|
||||
v8::MaybeLocal<v8::Value> Exec(
|
||||
Environment* env,
|
||||
const ada::url_pattern_input& input,
|
||||
std::optional<std::string_view>& baseURL); // NOLINT (runtime/references)
|
||||
bool Test(
|
||||
Environment* env,
|
||||
const ada::url_pattern_input& input,
|
||||
std::optional<std::string_view>& baseURL); // NOLINT (runtime/references)
|
||||
};
|
||||
|
||||
} // namespace node::url_pattern
|
||||
|
||||
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||
#endif // SRC_NODE_URL_PATTERN_H_
|
||||
|
|
@ -74,6 +74,7 @@ expected.beforePreExec = new Set([
|
|||
'NativeModule internal/querystring',
|
||||
'NativeModule querystring',
|
||||
'Internal Binding url',
|
||||
'Internal Binding url_pattern',
|
||||
'Internal Binding blob',
|
||||
'NativeModule internal/url',
|
||||
'NativeModule util',
|
||||
|
|
|
|||
2
typings/globals.d.ts
vendored
2
typings/globals.d.ts
vendored
|
|
@ -14,6 +14,7 @@ import { SymbolsBinding } from './internalBinding/symbols';
|
|||
import { TimersBinding } from './internalBinding/timers';
|
||||
import { TypesBinding } from './internalBinding/types';
|
||||
import { URLBinding } from './internalBinding/url';
|
||||
import { URLPatternBinding } from "./internalBinding/url_pattern";
|
||||
import { UtilBinding } from './internalBinding/util';
|
||||
import { WASIBinding } from './internalBinding/wasi';
|
||||
import { WorkerBinding } from './internalBinding/worker';
|
||||
|
|
@ -38,6 +39,7 @@ interface InternalBindingMap {
|
|||
timers: TimersBinding;
|
||||
types: TypesBinding;
|
||||
url: URLBinding;
|
||||
url_pattern: URLPatternBinding;
|
||||
util: UtilBinding;
|
||||
wasi: WASIBinding;
|
||||
worker: WorkerBinding;
|
||||
|
|
|
|||
20
typings/internalBinding/url_pattern.d.ts
vendored
Normal file
20
typings/internalBinding/url_pattern.d.ts
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
export class URLPattern {
|
||||
protocol: string
|
||||
username: string
|
||||
password: string
|
||||
hostname: string
|
||||
port: string
|
||||
pathname: string
|
||||
search: string
|
||||
hash: string
|
||||
|
||||
constructor(input: Record<string, string> | string, options?: { ignoreCase: boolean });
|
||||
constructor(input: Record<string, string> | string, baseUrl?: string, options?: { ignoreCase: boolean });
|
||||
|
||||
exec(input: string | Record<string, string>, baseURL?: string): null | Record<string, unknown>;
|
||||
test(input: string | Record<string, string>, baseURL?: string): boolean;
|
||||
}
|
||||
|
||||
export interface URLPatternBinding {
|
||||
URLPattern: URLPattern;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user