fix: retain resolved component paths

This commit is contained in:
root
2026-07-13 21:26:01 +00:00
parent 92baaa6299
commit 8da6cad305
5 changed files with 29 additions and 1 deletions
+5
View File
@@ -375,6 +375,11 @@ header free-functions are `inline`. The wasm backend exposes only declarations
old mutation handler, and verifies demand-priority convergence. A CLI request old mutation handler, and verifies demand-priority convergence. A CLI request
still waits and returns only the current dependency result after rebuild still waits and returns only the current dependency result after rebuild
completion. completion.
The component page also calls a parent once without its optional relative
child and again with that child activated. This guards the component-slot
cache invariant that a cache hit must restore both the function-table slot
and the resolved unit path; otherwise nested relative targets resolve from
the caller after a repeated parent invocation.
- **WebSocket end-to-end**: a headless client performs a raw WS handshake to - **WebSocket end-to-end**: a headless client performs a raw WS handshake to
`:HTTP_PORT` with path `/site/tests/websockets.ws.uce` (self-resolving `:HTTP_PORT` with path `/site/tests/websockets.ws.uce` (self-resolving
+6 -1
View File
@@ -23,6 +23,10 @@ RENDER(Request& context)
bool exists = component_exists("components/panel"); bool exists = component_exists("components/panel");
String resolved = component_resolve("components/panel"); String resolved = component_resolve("components/panel");
String panel_markup = component("components/panel", props, context); String panel_markup = component("components/panel", props, context);
DValue lazy_props;
String lazy_first = component("components/lazy-parent", lazy_props, context);
lazy_props["show_child"].set_bool(true);
String lazy_second = component("components/lazy-parent", lazy_props, context);
String alias_markup = component("components/props_alias", props, context); String alias_markup = component("components/props_alias", props, context);
// Named handler through the string-returning component("unit:NAME") form // Named handler through the string-returning component("unit:NAME") form
// (distinct path from component_render below): resolves __uce_component_HEADER // (distinct path from component_render below): resolves __uce_component_HEADER
@@ -42,6 +46,7 @@ RENDER(Request& context)
check("component_exists()", exists, exists ? "components/panel resolved as existing" : "components/panel missing"); check("component_exists()", exists, exists ? "components/panel resolved as existing" : "components/panel missing");
check("component_resolve()", resolved != "", resolved); check("component_resolve()", resolved != "", resolved);
check("component()", panel_markup.find("Component Output") != String::npos && panel_markup.find("This body comes from component()") != String::npos, panel_markup); check("component()", panel_markup.find("Component Output") != String::npos && panel_markup.find("This body comes from component()") != String::npos, panel_markup);
check("cached component retains path for a newly activated relative child", lazy_first.find("lazy-child") == String::npos && lazy_second.find("lazy-child") != String::npos, lazy_second);
check("COMPONENT(Request& props) alias", alias_markup.find("alias=Component Output") != String::npos && alias_markup.find("body=This body comes from component()") != String::npos, alias_markup); check("COMPONENT(Request& props) alias", alias_markup.find("alias=Component Output") != String::npos && alias_markup.find("body=This body comes from component()") != String::npos, alias_markup);
check("component_render() named handler", footer_markup.find("Named footer render") != String::npos, footer_markup); check("component_render() named handler", footer_markup.find("Named footer render") != String::npos, footer_markup);
check("component(\"unit:NAME\") named handler", header_markup.find("Component Output") != String::npos && header_markup.find("This body comes from component()") == String::npos, header_markup); check("component(\"unit:NAME\") named handler", header_markup.find("Component Output") != String::npos && header_markup.find("This body comes from component()") == String::npos, header_markup);
@@ -55,4 +60,4 @@ RENDER(Request& context)
site_tests_summary(passed, failed, skipped, "The panel fixture under site/tests/components exercises default and named component entry points."); site_tests_summary(passed, failed, skipped, "The panel fixture under site/tests/components exercises default and named component entry points.");
site_tests_page_end(); site_tests_page_end();
} }
+4
View File
@@ -0,0 +1,4 @@
COMPONENT(Request& context)
{
<><span>lazy-child</span></>
}
+5
View File
@@ -0,0 +1,5 @@
COMPONENT(Request& context)
{
if(context.props["show_child"].to_bool())
print(component("lazy-child", context.props, context));
}
+9
View File
@@ -458,6 +458,7 @@ extern "C" int32_t uce_host_component_resolve(
// target → table slot, reset per request (workspaces die with the request, // target → table slot, reset per request (workspaces die with the request,
// but a single workspace can render the same component many times) // but a single workspace can render the same component many times)
static std::map<String, s32> wasm_component_slots; static std::map<String, s32> wasm_component_slots;
static std::map<String, String> wasm_component_paths;
// These mirror small page-runtime pieces that cannot include compiler.cpp in // These mirror small page-runtime pieces that cannot include compiler.cpp in
// the wasm core (compiler.cpp owns parser/clang/cache bookkeeping for the host // the wasm core (compiler.cpp owns parser/clang/cache bookkeeping for the host
@@ -521,7 +522,11 @@ static s32 wasm_resolve_target(String unit_target, String handler, String* resol
bool is_exists = (handler == "exists"); bool is_exists = (handler == "exists");
auto cached = wasm_component_slots.find(cache_key); auto cached = wasm_component_slots.find(cache_key);
if(cached != wasm_component_slots.end() && !is_exists) if(cached != wasm_component_slots.end() && !is_exists)
{
if(resolved_out)
*resolved_out = wasm_component_paths[cache_key];
return(cached->second); return(cached->second);
}
char resolved[512]; char resolved[512];
String current = context ? context->resources.current_unit_file : ""; String current = context ? context->resources.current_unit_file : "";
s32 slot = uce_host_component_resolve( s32 slot = uce_host_component_resolve(
@@ -531,7 +536,10 @@ static s32 wasm_resolve_target(String unit_target, String handler, String* resol
if(resolved_out && slot) if(resolved_out && slot)
*resolved_out = String(resolved, strnlen(resolved, sizeof(resolved))); *resolved_out = String(resolved, strnlen(resolved, sizeof(resolved)));
if(!is_exists) if(!is_exists)
{
wasm_component_slots[cache_key] = slot; wasm_component_slots[cache_key] = slot;
wasm_component_paths[cache_key] = slot ? String(resolved, strnlen(resolved, sizeof(resolved))) : String("");
}
return(slot); return(slot);
} }
@@ -768,6 +776,7 @@ void uce_wasm_core_reset_request()
wasm_request.out = ""; wasm_request.out = "";
wasm_request.resources.current_unit_file = ""; wasm_request.resources.current_unit_file = "";
wasm_component_slots.clear(); wasm_component_slots.clear();
wasm_component_paths.clear();
} }
// Host pushes the UCEB2-encoded request context into a guest buffer // Host pushes the UCEB2-encoded request context into a guest buffer