fix(jsx): preserve context when using await before html helper#4662
fix(jsx): preserve context when using await before html helper#4662yusukebe merged 3 commits intohonojs:mainfrom
Conversation
When an async component used await before returning html with children, the context was being popped synchronously in the finally block before the Promise resolved. This caused children to lose access to the context. The fix delays the pop() until the Promise resolves (or rejects), ensuring context remains available throughout the async component's execution. Fixes honojs#4582
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4662 +/- ##
=======================================
Coverage 91.43% 91.43%
=======================================
Files 173 173
Lines 11373 11377 +4
Branches 3296 3298 +2
=======================================
+ Hits 10399 10403 +4
Misses 973 973
Partials 1 1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Hi @kaigritun Thank you for creating the pull request. It's a huge help! I believe the proposed changes will fix the issue correctly. I'd like to reduce the number of diff --git i/src/jsx/context.ts w/src/jsx/context.ts
index be4a8a99..dd32d7d3 100644
--- i/src/jsx/context.ts
+++ w/src/jsx/context.ts
@@ -30,16 +30,11 @@ export const createContext = <T>(defaultValue: T): Context<T> => {
}
if (string instanceof Promise) {
- return string.then(
- (resString) => {
+ return string
+ .then((resString) => raw(resString, (resString as HtmlEscapedString).callbacks))
+ .finally(() => {
values.pop()
- return raw(resString, (resString as HtmlEscapedString).callbacks)
- },
- (e) => {
- values.pop()
- throw e
- }
- )
+ })
} else {
values.pop()
return raw(string) |
|
Refactored to use return string
.finally(() => values.pop())
.then((resString) => raw(resString, ...))This removes the duplicate |
|
Hi @kaigritun Thanks for the changes. I think it's good. It seems package-lock.json was added by mistake, so please delete it. |
|
Done, removed package-lock.json. Thanks for the review! |
|
@kaigritun @usualoma Thank you! |
|
I'm not making any claims about the quality of their work, but I wanted to let you know that @kaigritun is a fully-autonomous non-human actor |
Summary
Fixes #4582
When an async component used
awaitbefore returninghtml\...`with children that access context viauseContext, the context was being popped synchronously in thefinally` block before the Promise resolved.Root Cause
In
src/jsx/context.ts, thecreateContextfunction uses afinallyblock to pop the context value after rendering children. However, when.toString()returns a Promise (async components), thefinallyblock executes immediately before the Promise resolves, causing descendant components to lose access to the context.Before (broken)
After (fixed)
Reproduction
Testing