diff --git a/src/node_url_pattern.cc b/src/node_url_pattern.cc index 571413b47da12d..17364f4a554015 100644 --- a/src/node_url_pattern.cc +++ b/src/node_url_pattern.cc @@ -409,22 +409,28 @@ std::optional URLPattern::URLPatternInit::FromJsObject( } MaybeLocal URLPattern::URLPatternComponentResult::ToJSObject( - Environment* env, const ada::url_pattern_component_result& result) { + Environment* env, + const ada::url_pattern_component_result& result, + const std::vector& ordered_group_names) { auto isolate = env->isolate(); auto context = env->context(); - LocalVector group_names(isolate); + LocalVector js_group_names(isolate); LocalVector group_values(isolate); - group_names.reserve(result.groups.size()); + js_group_names.reserve(result.groups.size()); group_values.reserve(result.groups.size()); - for (const auto& [group_key, group_value] : result.groups) { + for (const auto& group_key : ordered_group_names) { + const auto group = result.groups.find(group_key); + if (group == result.groups.end()) { + continue; + } Local key; if (!ToV8Value(context, group_key).ToLocal(&key)) { return {}; } - group_names.push_back(key.As()); + js_group_names.push_back(key.As()); Local value; - if (group_value) { - if (!ToV8Value(env->context(), *group_value).ToLocal(&value)) { + if (group->second) { + if (!ToV8Value(env->context(), *group->second).ToLocal(&value)) { return {}; } } else { @@ -434,9 +440,9 @@ MaybeLocal URLPattern::URLPatternComponentResult::ToJSObject( } auto parsed_group = Object::New(isolate, Object::New(isolate), - group_names.data(), + js_group_names.data(), group_values.data(), - group_names.size()); + js_group_names.size()); Local input; if (!ToV8Value(env->context(), result.input).ToLocal(&input)) { @@ -446,32 +452,34 @@ MaybeLocal URLPattern::URLPatternComponentResult::ToJSObject( auto tmpl = env->urlpatterncomponentresult_template(); if (tmpl.IsEmpty()) { static constexpr std::string_view namesVec[] = { - "input", "groups", + "input", }; tmpl = DictionaryTemplate::New(isolate, namesVec); env->set_urlpatterncomponentresult_template(tmpl); } - MaybeLocal values[] = {input, parsed_group}; + MaybeLocal values[] = {parsed_group, input}; return NewDictionaryInstance(env->context(), tmpl, values); } MaybeLocal URLPattern::URLPatternResult::ToJSValue( - Environment* env, const ada::url_pattern_result& result) { + Environment* env, + const ada::url_pattern_result& result, + const ada::url_pattern& url_pattern) { auto isolate = env->isolate(); auto tmpl = env->urlpatternresult_template(); if (tmpl.IsEmpty()) { static constexpr std::string_view namesVec[] = { + "hash", + "hostname", "inputs", - "protocol", - "username", "password", - "hostname", - "port", "pathname", + "port", + "protocol", "search", - "hash", + "username", }; tmpl = DictionaryTemplate::New(isolate, namesVec); env->set_urlpatternresult_template(tmpl); @@ -479,6 +487,10 @@ MaybeLocal URLPattern::URLPatternResult::ToJSValue( size_t index = 0; MaybeLocal vals[] = { + URLPatternComponentResult::ToJSObject( + env, result.hash, url_pattern.hash_component.group_name_list), + URLPatternComponentResult::ToJSObject( + env, result.hostname, url_pattern.hostname_component.group_name_list), Array::New(env->context(), result.inputs.size(), [&index, &inputs = result.inputs, env]() { @@ -493,14 +505,20 @@ MaybeLocal URLPattern::URLPatternResult::ToJSValue( return URLPatternInit::ToJsObject(env, init); } }), - URLPatternComponentResult::ToJSObject(env, result.protocol), - URLPatternComponentResult::ToJSObject(env, result.username), - URLPatternComponentResult::ToJSObject(env, result.password), - URLPatternComponentResult::ToJSObject(env, result.hostname), - URLPatternComponentResult::ToJSObject(env, result.port), - URLPatternComponentResult::ToJSObject(env, result.pathname), - URLPatternComponentResult::ToJSObject(env, result.search), - URLPatternComponentResult::ToJSObject(env, result.hash)}; + URLPatternComponentResult::ToJSObject( + env, result.password, url_pattern.password_component.group_name_list), + URLPatternComponentResult::ToJSObject( + env, result.pathname, url_pattern.pathname_component.group_name_list), + URLPatternComponentResult::ToJSObject( + env, result.port, url_pattern.port_component.group_name_list), + URLPatternComponentResult::ToJSObject( + env, result.protocol, url_pattern.protocol_component.group_name_list), + URLPatternComponentResult::ToJSObject( + env, result.search, url_pattern.search_component.group_name_list), + URLPatternComponentResult::ToJSObject( + env, + result.username, + url_pattern.username_component.group_name_list)}; return NewDictionaryInstanceNullProto(env->context(), tmpl, vals); } @@ -552,7 +570,7 @@ MaybeLocal URLPattern::Exec(Environment* env, std::optional& baseURL) { if (auto result = url_pattern_.exec(input, baseURL ? &*baseURL : nullptr)) { if (result->has_value()) { - return URLPatternResult::ToJSValue(env, result->value()); + return URLPatternResult::ToJSValue(env, result->value(), url_pattern_); } return Null(env->isolate()); } diff --git a/src/node_url_pattern.h b/src/node_url_pattern.h index 48f3956cb74458..02e61c6241a28e 100644 --- a/src/node_url_pattern.h +++ b/src/node_url_pattern.h @@ -10,7 +10,9 @@ #include #include +#include #include +#include namespace node::url_pattern { @@ -81,13 +83,17 @@ class URLPattern : public BaseObject { class URLPatternResult { public: static v8::MaybeLocal ToJSValue( - Environment* env, const ada::url_pattern_result& result); + Environment* env, + const ada::url_pattern_result& result, + const ada::url_pattern& url_pattern); }; class URLPatternComponentResult { public: static v8::MaybeLocal ToJSObject( - Environment* env, const ada::url_pattern_component_result& result); + Environment* env, + const ada::url_pattern_component_result& result, + const std::vector& ordered_group_names); }; private: diff --git a/test/parallel/test-urlpattern.js b/test/parallel/test-urlpattern.js index 96c431f5b87f69..638bba916e39c4 100644 --- a/test/parallel/test-urlpattern.js +++ b/test/parallel/test-urlpattern.js @@ -26,3 +26,38 @@ assert.throws(() => { }, { message: 'boom' }); + +{ + const result = new URLPattern({ pathname: '/:value' }) + .exec('https://example.com/test'); + + assert.deepStrictEqual(Object.keys(result), [ + 'hash', + 'hostname', + 'inputs', + 'password', + 'pathname', + 'port', + 'protocol', + 'search', + 'username', + ]); + assert.deepStrictEqual(Object.keys(result.pathname), [ + 'groups', + 'input', + ]); + assert.strictEqual(result.hostname.input, 'example.com'); + assert.strictEqual(result.pathname.input, '/test'); + assert.strictEqual(result.pathname.groups.value, 'test'); +} + +{ + const result = new URLPattern({ pathname: '/:one/:two/:three' }) + .exec('https://example.com/a/b/c'); + + assert.deepStrictEqual(Object.entries(result.pathname.groups), [ + ['one', 'a'], + ['two', 'b'], + ['three', 'c'], + ]); +}