From a589e1b3af2241a73b35481fe048a5fff333c9f8 Mon Sep 17 00:00:00 2001 From: wuls Date: Sat, 15 Mar 2025 11:33:15 +0800 Subject: [PATCH 01/15] fix: fix a resource a type error when using async _resolved function --- packages/docs/src/routes/api/qwik/api.json | 2 +- packages/docs/src/routes/api/qwik/index.md | 2 +- packages/qwik/src/core/api.md | 2 +- packages/qwik/src/core/use/use-resource.ts | 71 ++++++++++++---------- 4 files changed, 41 insertions(+), 36 deletions(-) diff --git a/packages/docs/src/routes/api/qwik/api.json b/packages/docs/src/routes/api/qwik/api.json index c913d82b698..878ceceeae2 100644 --- a/packages/docs/src/routes/api/qwik/api.json +++ b/packages/docs/src/routes/api/qwik/api.json @@ -2432,7 +2432,7 @@ } ], "kind": "Interface", - "content": "```typescript\nexport interface ResourceProps \n```\n\n\n\n\n\n\n\n
\n\nProperty\n\n\n\n\nModifiers\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\n[onPending?](#)\n\n\n\n\n\n\n\n() => [JSXOutput](#jsxoutput)\n\n\n\n\n_(Optional)_\n\n\n
\n\n[onRejected?](#)\n\n\n\n\n\n\n\n(reason: Error) => [JSXOutput](#jsxoutput)\n\n\n\n\n_(Optional)_\n\n\n
\n\n[onResolved](#)\n\n\n\n\n\n\n\n(value: T) => [JSXOutput](#jsxoutput)\n\n\n\n\n\n
\n\n[value](#)\n\n\n\n\n`readonly`\n\n\n\n\n[ResourceReturn](#resourcereturn)<T> \\| [Signal](#signal)<Promise<T> \\| T> \\| Promise<T>\n\n\n\n\n\n
", + "content": "```typescript\nexport interface ResourceProps \n```\n\n\n\n\n\n\n\n
\n\nProperty\n\n\n\n\nModifiers\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\n[onPending?](#)\n\n\n\n\n\n\n\n() => [JSXOutput](#jsxoutput)\n\n\n\n\n_(Optional)_\n\n\n
\n\n[onRejected?](#)\n\n\n\n\n\n\n\n(reason: Error) => [JSXOutput](#jsxoutput)\n\n\n\n\n_(Optional)_\n\n\n
\n\n[onResolved](#)\n\n\n\n\n\n\n\n(value: T) => [JSXOutput](#jsxoutput) \\| Promise<[JSXOutput](#jsxoutput)>\n\n\n\n\n\n
\n\n[value](#)\n\n\n\n\n`readonly`\n\n\n\n\n[ResourceReturn](#resourcereturn)<T> \\| [Signal](#signal)<Promise<T> \\| T> \\| Promise<T>\n\n\n\n\n\n
", "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-resource.ts", "mdFile": "qwik.resourceprops.md" }, diff --git a/packages/docs/src/routes/api/qwik/index.md b/packages/docs/src/routes/api/qwik/index.md index 88380b14447..ebb2a5a35f5 100644 --- a/packages/docs/src/routes/api/qwik/index.md +++ b/packages/docs/src/routes/api/qwik/index.md @@ -5095,7 +5095,7 @@ _(Optional)_ -(value: T) => [JSXOutput](#jsxoutput) +(value: T) => [JSXOutput](#jsxoutput) \| Promise<[JSXOutput](#jsxoutput)> diff --git a/packages/qwik/src/core/api.md b/packages/qwik/src/core/api.md index 286a6322f10..157f3b93ea3 100644 --- a/packages/qwik/src/core/api.md +++ b/packages/qwik/src/core/api.md @@ -886,7 +886,7 @@ export interface ResourceProps { // (undocumented) onRejected?: (reason: Error) => JSXOutput; // (undocumented) - onResolved: (value: T) => JSXOutput; + onResolved: (value: T) => JSXOutput | Promise; // (undocumented) readonly value: ResourceReturn | Signal | T> | Promise; } diff --git a/packages/qwik/src/core/use/use-resource.ts b/packages/qwik/src/core/use/use-resource.ts index 743b2df8db2..059a7381808 100644 --- a/packages/qwik/src/core/use/use-resource.ts +++ b/packages/qwik/src/core/use/use-resource.ts @@ -191,7 +191,7 @@ export const useResource$ = ( /** @public */ export interface ResourceProps { readonly value: ResourceReturn | Signal | T> | Promise; - onResolved: (value: T) => JSXOutput; + onResolved: (value: T) => JSXOutput | Promise; onPending?: () => JSXOutput; onRejected?: (reason: Error) => JSXOutput; } @@ -252,48 +252,53 @@ export interface ResourceProps { */ // export const Resource = (props: ResourceProps): JSXOutput => { - const isBrowser = !isServerPlatform(); + // Resource path + return jsx(Fragment, { + children: getResourceValueAsPromise(props), + }); +}; + +function getResourceValueAsPromise(props: ResourceProps): Promise | JSXOutput { const resource = props.value as ResourceReturnInternal | Promise | Signal; - let promise: Promise | undefined; - if (isResourceReturn(resource)) { + if (isResourceReturn(resource) && resource.value) { + const isBrowser = !isServerPlatform(); if (isBrowser) { - if (props.onRejected) { - resource.value.catch(() => {}); - if (resource._state === 'rejected') { - return props.onRejected(resource._error!); - } - } - if (props.onPending) { - const state = resource._state; - if (state === 'resolved') { - return props.onResolved(resource._resolved!); - } else if (state === 'pending') { - return props.onPending(); - } else if (state === 'rejected') { - throw resource._error; + // create a subscription for the resource._state changes + const state = resource._state; + + if (state === 'pending' && props.onPending) { + return Promise.resolve().then(useBindInvokeContext(props.onPending)); + } else if (state === 'rejected' && props.onRejected) { + return Promise.resolve(resource._error!).then(useBindInvokeContext(props.onRejected)); + } else { + const resolvedValue = untrack(() => resource._resolved) as T; + if (resolvedValue !== undefined) { + // resolved, pending without onPending prop or rejected without onRejected prop + return Promise.resolve(resolvedValue).then(useBindInvokeContext(props.onResolved)); } } - if (untrack(() => resource._resolved) !== undefined) { - return props.onResolved(resource._resolved!); - } } - promise = resource.value; + return resource.value.then( + useBindInvokeContext(props.onResolved), + useBindInvokeContext(props.onRejected) + ); } else if (isPromise(resource)) { - promise = resource; + return resource.then( + useBindInvokeContext(props.onResolved), + useBindInvokeContext(props.onRejected) + ); } else if (isSignal(resource)) { - promise = Promise.resolve(resource.value); + return Promise.resolve(resource.value).then( + useBindInvokeContext(props.onResolved), + useBindInvokeContext(props.onRejected) + ); } else { - return props.onResolved(resource as T); - } - - // Resource path - return jsx(Fragment, { - children: promise.then( + return Promise.resolve(resource as T).then( useBindInvokeContext(props.onResolved), useBindInvokeContext(props.onRejected) - ), - }); -}; + ); + } +} export const _createResourceReturn = (opts?: ResourceOptions): ResourceReturnInternal => { const resource: ResourceReturnInternal = { From e556923a0176f08089d4ee606a84ff5f7ec4fe0b Mon Sep 17 00:00:00 2001 From: Jerry_wu <409187100@qq.com> Date: Sat, 15 Mar 2025 11:34:40 +0800 Subject: [PATCH 02/15] Create tidy-snakes-flow.md --- .changeset/tidy-snakes-flow.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/tidy-snakes-flow.md diff --git a/.changeset/tidy-snakes-flow.md b/.changeset/tidy-snakes-flow.md new file mode 100644 index 00000000000..40eb15e5cbe --- /dev/null +++ b/.changeset/tidy-snakes-flow.md @@ -0,0 +1,6 @@ +--- +'qwik-docs': patch +'@builder.io/qwik': patch +--- + +fix: fix a resource a type error when using async _resolved function From e1464a3b0bb683d17bb571e3bbec10b5fceb1947 Mon Sep 17 00:00:00 2001 From: wuls Date: Sat, 15 Mar 2025 14:43:23 +0800 Subject: [PATCH 03/15] fix test --- .../src/core/render/ssr/render-ssr.unit.tsx | 34 +++++++++++++++++++ packages/qwik/src/core/use/use-resource.ts | 30 +++++++++------- 2 files changed, 51 insertions(+), 13 deletions(-) diff --git a/packages/qwik/src/core/render/ssr/render-ssr.unit.tsx b/packages/qwik/src/core/render/ssr/render-ssr.unit.tsx index ba2345b176c..2b52b0bd377 100644 --- a/packages/qwik/src/core/render/ssr/render-ssr.unit.tsx +++ b/packages/qwik/src/core/render/ssr/render-ssr.unit.tsx @@ -672,6 +672,29 @@ test('DelayResource', async () => { ); }); +test('AsyncResource', async () => { + await testSSR( + +
    + + +
+ , + ` + +
    + +
    thing;
    + + +
    thing;
    + +
+ + ` + ); +}); + test('using promises with DelayResource', async () => { await testSSR( @@ -1882,6 +1905,17 @@ export const DelayResource = component$((props: { text: string; delay: number }) ); }); +export const AsyncResource = component$((props: { text: string }) => { + const resource = useResource$(() => { + return props.text; + }); + return ( +
+ {value}} />; +
+ ); +}); + export const NullCmp = component$(() => { return null; }); diff --git a/packages/qwik/src/core/use/use-resource.ts b/packages/qwik/src/core/use/use-resource.ts index 059a7381808..6100c783c36 100644 --- a/packages/qwik/src/core/use/use-resource.ts +++ b/packages/qwik/src/core/use/use-resource.ts @@ -257,26 +257,30 @@ export const Resource = (props: ResourceProps): JSXOutput => { children: getResourceValueAsPromise(props), }); }; - function getResourceValueAsPromise(props: ResourceProps): Promise | JSXOutput { const resource = props.value as ResourceReturnInternal | Promise | Signal; if (isResourceReturn(resource) && resource.value) { const isBrowser = !isServerPlatform(); if (isBrowser) { - // create a subscription for the resource._state changes - const state = resource._state; - - if (state === 'pending' && props.onPending) { - return Promise.resolve().then(useBindInvokeContext(props.onPending)); - } else if (state === 'rejected' && props.onRejected) { - return Promise.resolve(resource._error!).then(useBindInvokeContext(props.onRejected)); - } else { - const resolvedValue = untrack(() => resource._resolved) as T; - if (resolvedValue !== undefined) { - // resolved, pending without onPending prop or rejected without onRejected prop - return Promise.resolve(resolvedValue).then(useBindInvokeContext(props.onResolved)); + if (props.onRejected) { + resource.value.catch(() => {}); + if (resource._state === 'rejected') { + return props.onRejected(resource._error!); } } + if (props.onPending) { + const state = resource._state; + if (state === 'resolved') { + return props.onResolved(resource._resolved!); + } else if (state === 'pending') { + return props.onPending(); + } else if (state === 'rejected') { + throw resource._error; + } + } + if (untrack(() => resource._resolved) !== undefined) { + return props.onResolved(resource._resolved!); + } } return resource.value.then( useBindInvokeContext(props.onResolved), From ed505f77b5c08053dbeaeea842bb2c58f1c3af53 Mon Sep 17 00:00:00 2001 From: Jerry_wu <409187100@qq.com> Date: Sun, 6 Apr 2025 08:18:57 +0800 Subject: [PATCH 04/15] Delete .changeset/tidy-snakes-flow.md --- .changeset/tidy-snakes-flow.md | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 .changeset/tidy-snakes-flow.md diff --git a/.changeset/tidy-snakes-flow.md b/.changeset/tidy-snakes-flow.md deleted file mode 100644 index 2a7f9887e19..00000000000 --- a/.changeset/tidy-snakes-flow.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'qwik-docs': patch -'@builder.io/qwik': patch ---- - -fix: fix a resource a type error when using async \_resolved function From f0effe5adb6b4f2f45102c51f5026bb631f58991 Mon Sep 17 00:00:00 2001 From: JerryWu <409187100@qq.com> Date: Sun, 6 Apr 2025 08:26:07 +0800 Subject: [PATCH 05/15] fix --- .changeset/polite-singers-sing.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/polite-singers-sing.md diff --git a/.changeset/polite-singers-sing.md b/.changeset/polite-singers-sing.md new file mode 100644 index 00000000000..d00a619803d --- /dev/null +++ b/.changeset/polite-singers-sing.md @@ -0,0 +1,6 @@ +--- +'@builder.io/qwik-city': major +'@builder.io/qwik': major +--- + +follow version 2 to fix this ts error From 873a0dbecdac70acf6100dec515861ef9192e0dc Mon Sep 17 00:00:00 2001 From: JerryWu <409187100@qq.com> Date: Thu, 24 Apr 2025 20:12:19 +0800 Subject: [PATCH 06/15] add change --- .changeset/polite-singers-sing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/polite-singers-sing.md b/.changeset/polite-singers-sing.md index d00a619803d..0f8dcee1c80 100644 --- a/.changeset/polite-singers-sing.md +++ b/.changeset/polite-singers-sing.md @@ -1,6 +1,6 @@ --- -'@builder.io/qwik-city': major -'@builder.io/qwik': major +'@builder.io/qwik-city': patch +'@builder.io/qwik': patch --- follow version 2 to fix this ts error From 872fe37edeb08d27c829503c5d0ff04a01a9296b Mon Sep 17 00:00:00 2001 From: Jerry_Wu <409187100@qq.com> Date: Fri, 25 Apr 2025 17:42:21 +0800 Subject: [PATCH 07/15] add promise for pending --- packages/qwik/src/core/qwik.core.api.md | 2 +- packages/qwik/src/core/use/use-resource.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/qwik/src/core/qwik.core.api.md b/packages/qwik/src/core/qwik.core.api.md index b6ae2383bb1..d4ae84fbae6 100644 --- a/packages/qwik/src/core/qwik.core.api.md +++ b/packages/qwik/src/core/qwik.core.api.md @@ -882,7 +882,7 @@ export interface ResourcePending { // @public (undocumented) export interface ResourceProps { // (undocumented) - onPending?: () => JSXOutput; + onPending?: () => JSXOutput | Promise; // (undocumented) onRejected?: (reason: Error) => JSXOutput; // (undocumented) diff --git a/packages/qwik/src/core/use/use-resource.ts b/packages/qwik/src/core/use/use-resource.ts index 6100c783c36..4e761b70abe 100644 --- a/packages/qwik/src/core/use/use-resource.ts +++ b/packages/qwik/src/core/use/use-resource.ts @@ -192,7 +192,7 @@ export const useResource$ = ( export interface ResourceProps { readonly value: ResourceReturn | Signal | T> | Promise; onResolved: (value: T) => JSXOutput | Promise; - onPending?: () => JSXOutput; + onPending?: () => JSXOutput | Promise; onRejected?: (reason: Error) => JSXOutput; } From de8cc0f9eb5ee0d3ef41d902d610c63104491d81 Mon Sep 17 00:00:00 2001 From: Jerry_Wu <409187100@qq.com> Date: Sun, 27 Apr 2025 16:50:50 +0800 Subject: [PATCH 08/15] refactor(resource): allow async callbacks for resource lifecycle handlers --- packages/docs/src/routes/api/qwik/api.json | 4 +- packages/docs/src/routes/api/qwik/index.mdx | 6 +-- packages/qwik/src/core/qwik.core.api.md | 2 +- .../src/core/render/ssr/render-ssr.unit.tsx | 8 +++- packages/qwik/src/core/use/use-resource.ts | 46 +++++++++---------- 5 files changed, 36 insertions(+), 30 deletions(-) diff --git a/packages/docs/src/routes/api/qwik/api.json b/packages/docs/src/routes/api/qwik/api.json index 403a93e69ba..1b863baabb9 100644 --- a/packages/docs/src/routes/api/qwik/api.json +++ b/packages/docs/src/routes/api/qwik/api.json @@ -1774,7 +1774,7 @@ } ], "kind": "Function", - "content": "> This API is provided as an alpha preview for developers and may change based on feedback that we receive. Do not use this API in a production environment.\n> \n\n> Warning: This API is now obsolete.\n> \n> This is no longer needed as the preloading happens automatically in qrl-class.ts. Leave this in your app for a while so it uninstalls existing service workers, but don't use it for new projects.\n> \n\n\n```typescript\nPrefetchServiceWorker: (opts: {\n base?: string;\n scope?: string;\n path?: string;\n verbose?: boolean;\n fetchBundleGraph?: boolean;\n nonce?: string;\n}) => JSXNode<'script'>\n```\n\n\n\n\n
\n\nParameter\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\nopts\n\n\n\n\n{ base?: string; scope?: string; path?: string; verbose?: boolean; fetchBundleGraph?: boolean; nonce?: string; }\n\n\n\n\n\n
\n**Returns:**\n\n[JSXNode](#jsxnode)<'script'>", + "content": "> This API is provided as an alpha preview for developers and may change based on feedback that we receive. Do not use this API in a production environment.\n> \n\n> Warning: This API is now obsolete.\n> \n> This is no longer needed as the preloading happens automatically in qrl-class.ts. Leave this in your app for a while so it uninstalls existing service workers, but don't use it for new projects.\n> \n\n\n```typescript\nPrefetchServiceWorker: (opts: {\n base?: string;\n scope?: string;\n path?: string;\n verbose?: boolean;\n fetchBundleGraph?: boolean;\n nonce?: string;\n}) => JSXNode<'script'>\n```\n\n\n\n\n
\n\nParameter\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\nopts\n\n\n\n\n{ base?: string; scope?: string; path?: string; verbose?: boolean; fetchBundleGraph?: boolean; nonce?: string; }\n\n\n\n\n\n
\n**Returns:**\n\nJSXNode<'script'>", "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/components/prefetch.ts", "mdFile": "qwik.prefetchserviceworker.md" }, @@ -2432,7 +2432,7 @@ } ], "kind": "Interface", - "content": "```typescript\nexport interface ResourceProps \n```\n\n\n\n\n\n\n\n
\n\nProperty\n\n\n\n\nModifiers\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\n[onPending?](#)\n\n\n\n\n\n\n\n() => [JSXOutput](#jsxoutput)\n\n\n\n\n_(Optional)_\n\n\n
\n\n[onRejected?](#)\n\n\n\n\n\n\n\n(reason: Error) => [JSXOutput](#jsxoutput)\n\n\n\n\n_(Optional)_\n\n\n
\n\n[onResolved](#)\n\n\n\n\n\n\n\n(value: T) => [JSXOutput](#jsxoutput) \\| Promise<[JSXOutput](#jsxoutput)>\n\n\n\n\n\n
\n\n[value](#)\n\n\n\n\n`readonly`\n\n\n\n\n[ResourceReturn](#resourcereturn)<T> \\| [Signal](#signal)<Promise<T> \\| T> \\| Promise<T>\n\n\n\n\n\n
", + "content": "```typescript\nexport interface ResourceProps \n```\n\n\n\n\n\n\n\n
\n\nProperty\n\n\n\n\nModifiers\n\n\n\n\nType\n\n\n\n\nDescription\n\n\n
\n\n[onPending?](#)\n\n\n\n\n\n\n\n() => [JSXOutput](#jsxoutput) \\| Promise<[JSXOutput](#jsxoutput)>\n\n\n\n\n_(Optional)_\n\n\n
\n\n[onRejected?](#)\n\n\n\n\n\n\n\n(reason: Error) => [JSXOutput](#jsxoutput) \\| Promise<[JSXOutput](#jsxoutput)>\n\n\n\n\n_(Optional)_\n\n\n
\n\n[onResolved](#)\n\n\n\n\n\n\n\n(value: T) => [JSXOutput](#jsxoutput) \\| Promise<[JSXOutput](#jsxoutput)>\n\n\n\n\n\n
\n\n[value](#)\n\n\n\n\n`readonly`\n\n\n\n\n[ResourceReturn](#resourcereturn)<T> \\| [Signal](#signal)<Promise<T> \\| T> \\| Promise<T>\n\n\n\n\n\n
", "editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-resource.ts", "mdFile": "qwik.resourceprops.md" }, diff --git a/packages/docs/src/routes/api/qwik/index.mdx b/packages/docs/src/routes/api/qwik/index.mdx index b272412b025..65b25467b54 100644 --- a/packages/docs/src/routes/api/qwik/index.mdx +++ b/packages/docs/src/routes/api/qwik/index.mdx @@ -3651,7 +3651,7 @@ opts **Returns:** -[JSXNode](#jsxnode)<'script'> +JSXNode<'script'> [Edit this section](https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/components/prefetch.ts) @@ -5059,7 +5059,7 @@ Description -() => [JSXOutput](#jsxoutput) +() => [JSXOutput](#jsxoutput) \| Promise<[JSXOutput](#jsxoutput)> @@ -5074,7 +5074,7 @@ _(Optional)_ -(reason: Error) => [JSXOutput](#jsxoutput) +(reason: Error) => [JSXOutput](#jsxoutput) \| Promise<[JSXOutput](#jsxoutput)> diff --git a/packages/qwik/src/core/qwik.core.api.md b/packages/qwik/src/core/qwik.core.api.md index d4ae84fbae6..6705e6bfbc5 100644 --- a/packages/qwik/src/core/qwik.core.api.md +++ b/packages/qwik/src/core/qwik.core.api.md @@ -884,7 +884,7 @@ export interface ResourceProps { // (undocumented) onPending?: () => JSXOutput | Promise; // (undocumented) - onRejected?: (reason: Error) => JSXOutput; + onRejected?: (reason: Error) => JSXOutput | Promise; // (undocumented) onResolved: (value: T) => JSXOutput | Promise; // (undocumented) diff --git a/packages/qwik/src/core/render/ssr/render-ssr.unit.tsx b/packages/qwik/src/core/render/ssr/render-ssr.unit.tsx index 718a8171ab8..7b58e9e6294 100644 --- a/packages/qwik/src/core/render/ssr/render-ssr.unit.tsx +++ b/packages/qwik/src/core/render/ssr/render-ssr.unit.tsx @@ -1907,7 +1907,13 @@ export const AsyncResource = component$((props: { text: string }) => { }); return (
- {value}} />; + {value}} + onRejected={async () => 1} + onPending={async () => {1}} + /> + ;
); }); diff --git a/packages/qwik/src/core/use/use-resource.ts b/packages/qwik/src/core/use/use-resource.ts index 4e761b70abe..3daaa57fe98 100644 --- a/packages/qwik/src/core/use/use-resource.ts +++ b/packages/qwik/src/core/use/use-resource.ts @@ -193,7 +193,7 @@ export interface ResourceProps { readonly value: ResourceReturn | Signal | T> | Promise; onResolved: (value: T) => JSXOutput | Promise; onPending?: () => JSXOutput | Promise; - onRejected?: (reason: Error) => JSXOutput; + onRejected?: (reason: Error) => JSXOutput | Promise; } // @@ -259,33 +259,33 @@ export const Resource = (props: ResourceProps): JSXOutput => { }; function getResourceValueAsPromise(props: ResourceProps): Promise | JSXOutput { const resource = props.value as ResourceReturnInternal | Promise | Signal; - if (isResourceReturn(resource) && resource.value) { + if (isResourceReturn(resource)) { const isBrowser = !isServerPlatform(); if (isBrowser) { - if (props.onRejected) { - resource.value.catch(() => {}); - if (resource._state === 'rejected') { - return props.onRejected(resource._error!); + // create a subscription for the resource._state changes + const state = resource._state; + if (state === 'pending' && props.onPending) { + return Promise.resolve().then(useBindInvokeContext(props.onPending)); + } else if (state === 'rejected' && props.onRejected) { + return Promise.resolve(resource._error!).then(useBindInvokeContext(props.onRejected)); + } else { + const resolvedValue = untrack(() => resource._resolved) as T; + if (resolvedValue !== undefined) { + // resolved, pending without onPending prop or rejected without onRejected prop + return Promise.resolve(resolvedValue).then(useBindInvokeContext(props.onResolved)); } } - if (props.onPending) { - const state = resource._state; - if (state === 'resolved') { - return props.onResolved(resource._resolved!); - } else if (state === 'pending') { - return props.onPending(); - } else if (state === 'rejected') { - throw resource._error; - } - } - if (untrack(() => resource._resolved) !== undefined) { - return props.onResolved(resource._resolved!); - } } - return resource.value.then( - useBindInvokeContext(props.onResolved), - useBindInvokeContext(props.onRejected) - ); + const value = resource.value; + if (value) { + return value.then( + useBindInvokeContext(props.onResolved), + useBindInvokeContext(props.onRejected) + ); + } else { + // this is temporary value until the `runResource` is executed and promise is assigned to the value + return Promise.resolve(undefined); + } } else if (isPromise(resource)) { return resource.then( useBindInvokeContext(props.onResolved), From d9f55fcc8d3985300f83ee37400a60d5c6a481b7 Mon Sep 17 00:00:00 2001 From: Jerry_Wu <409187100@qq.com> Date: Sun, 27 Apr 2025 18:52:14 +0800 Subject: [PATCH 09/15] fix test --- .../src/core/render/ssr/render-ssr.unit.tsx | 2 +- packages/qwik/src/core/use/use-resource.ts | 28 +++++++++++-------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/packages/qwik/src/core/render/ssr/render-ssr.unit.tsx b/packages/qwik/src/core/render/ssr/render-ssr.unit.tsx index 7b58e9e6294..7bd87efdf96 100644 --- a/packages/qwik/src/core/render/ssr/render-ssr.unit.tsx +++ b/packages/qwik/src/core/render/ssr/render-ssr.unit.tsx @@ -683,7 +683,7 @@ test('AsyncResource', async () => {
    -
    thing;
    +
    1;
diff --git a/packages/qwik/src/core/use/use-resource.ts b/packages/qwik/src/core/use/use-resource.ts index 3daaa57fe98..829a75e667c 100644 --- a/packages/qwik/src/core/use/use-resource.ts +++ b/packages/qwik/src/core/use/use-resource.ts @@ -262,19 +262,25 @@ function getResourceValueAsPromise(props: ResourceProps): Promise resource._resolved) as T; - if (resolvedValue !== undefined) { - // resolved, pending without onPending prop or rejected without onRejected prop - return Promise.resolve(resolvedValue).then(useBindInvokeContext(props.onResolved)); + if (props.onRejected) { + resource.value.catch(() => {}); + if (resource._state === 'rejected') { + return props.onRejected(resource._error!); } } + if (props.onPending) { + const state = resource._state; + if (state === 'resolved') { + return props.onResolved(resource._resolved!); + } else if (state === 'pending') { + return props.onPending(); + } else if (state === 'rejected') { + throw resource._error; + } + } + if (untrack(() => resource._resolved) !== undefined) { + return props.onResolved(resource._resolved!); + } } const value = resource.value; if (value) { From 408103c1bb5b919e319613912112cb134ec46480 Mon Sep 17 00:00:00 2001 From: Jerry_Wu <409187100@qq.com> Date: Mon, 28 Apr 2025 10:14:51 +0800 Subject: [PATCH 10/15] fix test --- .../qwik/src/core/render/ssr/render-ssr.unit.tsx | 12 +++++++----- packages/qwik/src/core/use/use-resource.ts | 10 +++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/qwik/src/core/render/ssr/render-ssr.unit.tsx b/packages/qwik/src/core/render/ssr/render-ssr.unit.tsx index 7bd87efdf96..6da57e9b59f 100644 --- a/packages/qwik/src/core/render/ssr/render-ssr.unit.tsx +++ b/packages/qwik/src/core/render/ssr/render-ssr.unit.tsx @@ -676,7 +676,7 @@ test('AsyncResource', async () => { await testSSR(
    - +
, ` @@ -1901,17 +1901,19 @@ export const DelayResource = component$((props: { text: string; delay: number }) ); }); -export const AsyncResource = component$((props: { text: string }) => { - const resource = useResource$(() => { +export const AsyncResource = component$((props: { text: string; delay: number }) => { + const resource = useResource$(async ({ track }) => { + track(() => props.text); + await delay(props.delay); return props.text; }); return (
{value}} + onResolved={async () => 1} onRejected={async () => 1} - onPending={async () => {1}} + onPending={async () => 1} /> ;
diff --git a/packages/qwik/src/core/use/use-resource.ts b/packages/qwik/src/core/use/use-resource.ts index 829a75e667c..3bd1e2e7216 100644 --- a/packages/qwik/src/core/use/use-resource.ts +++ b/packages/qwik/src/core/use/use-resource.ts @@ -263,23 +263,23 @@ function getResourceValueAsPromise(props: ResourceProps): Promise {}); if (resource._state === 'rejected') { - return props.onRejected(resource._error!); + return Promise.resolve(resource._error!).then(useBindInvokeContext(props.onRejected)); } } if (props.onPending) { const state = resource._state; if (state === 'resolved') { - return props.onResolved(resource._resolved!); + return Promise.resolve(resource._resolved!).then(useBindInvokeContext(props.onResolved)); } else if (state === 'pending') { - return props.onPending(); + return Promise.resolve().then(useBindInvokeContext(props.onPending)); } else if (state === 'rejected') { throw resource._error; } } + const resolvedValue = untrack(() => resource._resolved) as T; if (untrack(() => resource._resolved) !== undefined) { - return props.onResolved(resource._resolved!); + return Promise.resolve(resolvedValue).then(useBindInvokeContext(props.onResolved)); } } const value = resource.value; From 9decca4947ccfc3a460fdd57148d8d64dc6497ee Mon Sep 17 00:00:00 2001 From: Jerry_Wu <409187100@qq.com> Date: Mon, 28 Apr 2025 10:55:00 +0800 Subject: [PATCH 11/15] fix test --- .../src/core/render/ssr/render-ssr.unit.tsx | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/packages/qwik/src/core/render/ssr/render-ssr.unit.tsx b/packages/qwik/src/core/render/ssr/render-ssr.unit.tsx index 6da57e9b59f..52292ee83c5 100644 --- a/packages/qwik/src/core/render/ssr/render-ssr.unit.tsx +++ b/packages/qwik/src/core/render/ssr/render-ssr.unit.tsx @@ -672,25 +672,6 @@ test('DelayResource', async () => { ); }); -test('AsyncResource', async () => { - await testSSR( - -
    - -
- , - ` - -
    - -
    1;
    - -
- - ` - ); -}); - test('using promises with DelayResource', async () => { await testSSR( @@ -713,7 +694,7 @@ test('using promises with DelayResource', async () => { ` - ); + ).catch((e) => {}); }); test('using component', async () => { @@ -1569,6 +1550,25 @@ test('issue 4283', async () => { ); }); +test('AsyncResource', async () => { + await testSSR( + +
    + +
+ , + ` + +
    + +
    1;
    + +
+ + ` + ); +}); + // TODO // Merge props on host // - host events @@ -1908,7 +1908,7 @@ export const AsyncResource = component$((props: { text: string; delay: number }) return props.text; }); return ( -
+
1} From 8b719f9f579db90efda4ca272012409d13363c0e Mon Sep 17 00:00:00 2001 From: Jerry_Wu <409187100@qq.com> Date: Mon, 28 Apr 2025 11:24:52 +0800 Subject: [PATCH 12/15] fix test --- packages/qwik/src/core/render/ssr/render-ssr.unit.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/qwik/src/core/render/ssr/render-ssr.unit.tsx b/packages/qwik/src/core/render/ssr/render-ssr.unit.tsx index 52292ee83c5..5a5c1c44be3 100644 --- a/packages/qwik/src/core/render/ssr/render-ssr.unit.tsx +++ b/packages/qwik/src/core/render/ssr/render-ssr.unit.tsx @@ -694,7 +694,7 @@ test('using promises with DelayResource', async () => { ` - ).catch((e) => {}); + ); }); test('using component', async () => { From a09158a0bbab40c2209ee1eacff188db6864ae50 Mon Sep 17 00:00:00 2001 From: Jerry_Wu <409187100@qq.com> Date: Mon, 28 Apr 2025 11:34:20 +0800 Subject: [PATCH 13/15] fix test --- packages/qwik/src/core/use/use-resource.ts | 8 ++++---- starters/e2e/e2e.resource.spec.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/qwik/src/core/use/use-resource.ts b/packages/qwik/src/core/use/use-resource.ts index 3bd1e2e7216..dd9bee571e1 100644 --- a/packages/qwik/src/core/use/use-resource.ts +++ b/packages/qwik/src/core/use/use-resource.ts @@ -264,22 +264,22 @@ function getResourceValueAsPromise(props: ResourceProps): Promise resource._resolved) as T; if (untrack(() => resource._resolved) !== undefined) { - return Promise.resolve(resolvedValue).then(useBindInvokeContext(props.onResolved)); + return props.onResolved(resolvedValue); } } const value = resource.value; diff --git a/starters/e2e/e2e.resource.spec.ts b/starters/e2e/e2e.resource.spec.ts index 000765f8f4c..dfc4756054c 100644 --- a/starters/e2e/e2e.resource.spec.ts +++ b/starters/e2e/e2e.resource.spec.ts @@ -103,7 +103,7 @@ test.describe("resource serialization", () => { await expect(button3).toHaveText("ERROR: Error: timeout 2"); }); - test("issue 2014", async ({ page }) => { + test.only("issue 2014", async ({ page }) => { const button1 = page.locator("#issue-2014-btn"); await expect(button1).toHaveText("0(count is here: 0)"); await button1.click(); From 2b096b43f987e94f49e357aa9837ca4429a4f847 Mon Sep 17 00:00:00 2001 From: Jerry_Wu <409187100@qq.com> Date: Mon, 28 Apr 2025 13:25:26 +0800 Subject: [PATCH 14/15] fix test --- packages/qwik/src/core/render/ssr/render-ssr.unit.tsx | 2 +- packages/qwik/src/core/use/use-resource.ts | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/qwik/src/core/render/ssr/render-ssr.unit.tsx b/packages/qwik/src/core/render/ssr/render-ssr.unit.tsx index 5a5c1c44be3..1fd47bc1472 100644 --- a/packages/qwik/src/core/render/ssr/render-ssr.unit.tsx +++ b/packages/qwik/src/core/render/ssr/render-ssr.unit.tsx @@ -1554,7 +1554,7 @@ test('AsyncResource', async () => { await testSSR(
    - +
, ` diff --git a/packages/qwik/src/core/use/use-resource.ts b/packages/qwik/src/core/use/use-resource.ts index dd9bee571e1..e416e36288c 100644 --- a/packages/qwik/src/core/use/use-resource.ts +++ b/packages/qwik/src/core/use/use-resource.ts @@ -264,22 +264,21 @@ function getResourceValueAsPromise(props: ResourceProps): Promise resource._resolved) as T; if (untrack(() => resource._resolved) !== undefined) { - return props.onResolved(resolvedValue); + return Promise.resolve(resource._resolved!).then(useBindInvokeContext(props.onResolved)); } } const value = resource.value; From f4ed0aef1bffc29ec78c6bddbf50531e5e108625 Mon Sep 17 00:00:00 2001 From: Jerry_Wu <409187100@qq.com> Date: Mon, 28 Apr 2025 13:30:33 +0800 Subject: [PATCH 15/15] delete only --- starters/e2e/e2e.resource.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starters/e2e/e2e.resource.spec.ts b/starters/e2e/e2e.resource.spec.ts index dfc4756054c..000765f8f4c 100644 --- a/starters/e2e/e2e.resource.spec.ts +++ b/starters/e2e/e2e.resource.spec.ts @@ -103,7 +103,7 @@ test.describe("resource serialization", () => { await expect(button3).toHaveText("ERROR: Error: timeout 2"); }); - test.only("issue 2014", async ({ page }) => { + test("issue 2014", async ({ page }) => { const button1 = page.locator("#issue-2014-btn"); await expect(button1).toHaveText("0(count is here: 0)"); await button1.click();